Learn the core syntax of the Realtime Database Security Rules language

Firebase Realtime Database Security Rules allow you to control access to data stored in your database. The flexible rules syntax allows you to create rules that match anything, from all writes to your database to operations on individual nodes.

Realtime Database Security Rules are declarative configuration for your database. This means that the rules are defined separately from the product logic. This has a number of advantages: clients aren't responsible for enforcing security, buggy implementations will not compromise your data, and perhaps most importantly, there is no need for an intermediate referee, such as a server, to protect data from the world.

This topic describes the basic syntax and structure Realtime Database Security Rules used to create complete rulesets.

Structuring Your Security Rules

Realtime Database Security Rules are made up of JavaScript-like expressions contained in a JSON document. The structure of your rules should follow the structure of the data you have stored in your database.

Basic rules identify a set of nodes to be secured, the access methods (e.g., read, write) involved, and conditions under which access is either allowed or denied. In the following examples, our conditions will be simple true and false statements, but in the next topic we'll cover more dynamic ways to express conditions.

So, for example, if we are trying to secure a child_node under a parent_node , the general syntax to follow is:

{
  "rules": {
    "parent_node": {
      "child_node": {
        ".read": <condition>,
        ".write": <condition>,
        ".validate": <condition>,
      }
    }
  }
}

Let's apply this pattern. For example, let's say you are keeping track of a list of messages and have data that looks like this:

{
  "messages": {
    "message0": {
      "content": "Hello",
      "timestamp": 1405704370369
    },
    "message1": {
      "content": "Goodbye",
      "timestamp": 1405704395231
    },
    ...
  }
}

Your rules should be structured in a similar manner. Here's a set of rules for read-only security that might make sense for this data structure. This example illustrates how we specify database nodes to which rules apply and the conditions for evaluating rules at those nodes.

 { 
  
 "rules" 
 : 
  
 { 
  
 // 
  
 For 
  
 requests 
  
 to 
  
 access 
  
 the 
  
 'messages' 
  
 node 
 ... 
  
 "messages" 
 : 
  
 { 
  
 // 
  
 ... 
 and 
  
 the 
  
 individual 
  
 wildcarded 
  
 'message' 
  
 nodes 
  
 beneath 
  
 // 
  
 ( 
 we 
 'll cover wildcarding variables more a bit later).... 
  
 "$message" 
 : 
  
 { 
  
 // 
  
 For 
  
 each 
  
 message 
 , 
  
 allow 
  
 a 
  
 read 
  
 operation 
  
 if 
  
< condition 
> . 
  
 In 
  
 this 
  
 // 
  
 case 
 , 
  
 we 
  
 specify 
  
 our 
  
 condition 
  
 as 
  
 "true" 
 , 
  
 so 
  
 read 
  
 access 
  
 is 
  
 always 
  
 granted 
 . 
  
 ".read" 
 : 
  
 "true" 
 , 
  
 // 
  
 For 
  
 read 
 - 
 only 
  
 behavior 
 , 
  
 we 
  
 specify 
  
 that 
  
 for 
  
 write 
  
 operations 
 , 
  
 our 
  
 // 
  
 condition 
  
 is 
  
 false 
 . 
  
 ".write" 
 : 
  
 "false" 
  
 } 
  
 } 
  
 } 
 } 

Basic Rules Operations

There are three types of rules for enforcing security based on the type of operation being performed on the data: .write , .read , and .validate . Here is a quick summary of their purposes:

Rule Types
.read Describes if and when data is allowed to be read by users.
.write Describes if and when data is allowed to be written.
.validate Defines what a correctly formatted value will look like, whether it has child attributes, and the data type.

Wildcard Capture Variables

All rules statements point to nodes. A statement can point to a specific node or use $ wildcard capture variables to point to sets of nodes at a level of the hierarchy. Use these capture variables to store the value of node keys for use inside subsequent rules statements. This technique lets you write more complex Rules conditions , something we'll cover in more detail in the next topic.

 { 
  
 "rules" 
 : 
  
 { 
  
 "rooms" 
 : 
  
 { 
  
 // 
  
 this 
  
 rule 
  
 applies 
  
 to 
  
 any 
  
 child 
  
 of 
  
 / 
 rooms 
 / 
 , 
  
 the 
  
 key 
  
 for 
  
 each 
  
 room 
  
 id 
  
 // 
  
 is 
  
 stored 
  
 inside 
  
 $ 
 room_id 
  
 variable 
  
 for 
  
 reference 
  
 "$room_id" 
 : 
  
 { 
  
 "topic" 
 : 
  
 { 
  
 // 
  
 the 
  
 room 
 's topic can be changed if the room id has "public" in it 
  
 ".write" 
 : 
  
 "$room_id.contains('public')" 
  
 } 
  
 } 
  
 } 
  
 } 
 } 

The dynamic $ variables can also be used in parallel with constant path names. In this example, we're using the $other variable to declare a .validate rule that ensures that widget has no children other than title and color . Any write that would result in additional children being created would fail.

{
  "rules": {
    "widget": {
      // a widget can have a title or color attribute
      "title": { ".validate": true },
      "color": { ".validate": true },

      // but no other child paths are allowed
      // in this case, $other means any key excluding "title" and "color"
      "$other": { ".validate": false }
    }
  }
}

Read and Write Rules Cascade

.read and .write rules work from top-down, with shallower rules overriding deeper rules. If a rule grants read or write permissions at a particular path, then it also grants access to all child nodes under it. Consider the following structure:

{
  "rules": {
     "foo": {
        // allows read to /foo/*
        ".read": "data.child('baz').val() === true",
        "bar": {
          /* ignored, since read was allowed already */
          ".read": false
        }
     }
  }
}

This security structure allows /bar/ to be read from whenever /foo/ contains a child baz with value true . The ".read": false rule under /foo/bar/ has no effect here, since access cannot be revoked by a child path.

While it may not seem immediately intuitive, this is a powerful part of the rules language and allows for very complex access privileges to be implemented with minimal effort. This will be illustrated when we get into user-based security later in this guide.

Note that .validate rules do not cascade. All validate rules must be satisfied at all levels of the hierarchy in order for a write to be allowed.

Rules Are Not Filters

Rules are applied in an atomic manner. That means that a read or write operation is failed immediately if there isn't a rule at that location or at a parent location that grants access. Even if every affected child path is accessible, reading at the parent location will fail completely. Consider this structure:

{
  "rules": {
    "records": {
      "rec1": {
        ".read": true
      },
      "rec2": {
        ".read": false
      }
    }
  }
}

Without understanding that rules are evaluated atomically, it might seem like fetching the /records/ path would return rec1 but not rec2 . The actual result, however, is an error:

JavaScript
 var 
  
 db 
  
 = 
  
 firebase 
 . 
 database 
 (); 
 db 
 . 
 ref 
 ( 
 "records" 
 ). 
 once 
 ( 
 "value" 
 , 
  
 function 
 ( 
 snap 
 ) 
  
 { 
  
 // success method is not called 
 }, 
  
 function 
 ( 
 err 
 ) 
  
 { 
  
 // error callback triggered with PERMISSION_DENIED 
 }); 
Objective-C
Note: This Firebase product is not available on the App Clip target.
 FIRDatabaseReference 
  
 * 
 ref 
  
 = 
  
 [[ 
 FIRDatabase 
  
 database 
 ] 
  
 reference 
 ]; 
 [[ 
 _ref 
  
 child 
 : 
 @"records" 
 ] 
  
 observeSingleEventOfType 
 : 
 FIRDataEventTypeValue 
  
 withBlock 
 :^ 
 ( 
 FIRDataSnapshot 
  
 * 
 snapshot 
 ) 
  
 { 
  
 // success block is not called 
 } 
  
 withCancelBlock 
 :^ 
 ( 
 NSError 
  
 * 
  
 _Nonnull 
  
 error 
 ) 
  
 { 
  
 // cancel block triggered with PERMISSION_DENIED 
 }]; 
Swift
Note: This Firebase product is not available on the App Clip target.
 var 
  
 ref 
  
 = 
  
 FIRDatabase 
 . 
 database 
 (). 
 reference 
 () 
 ref 
 . 
 child 
 ( 
 "records" 
 ). 
 observeSingleEventOfType 
 (. 
 Value 
 , 
  
 withBlock 
 : 
  
 { 
  
 snapshot 
  
 in 
  
 // success block is not called 
 }, 
  
 withCancelBlock 
 : 
  
 { 
  
 error 
  
 in 
  
 // cancel block triggered with PERMISSION_DENIED 
 }) 
Java
 FirebaseDatabase 
  
 database 
  
 = 
  
 FirebaseDatabase 
 . 
 getInstance 
 (); 
 DatabaseReference 
  
 ref 
  
 = 
  
 database 
 . 
 getReference 
 ( 
 "records" 
 ); 
 ref 
 . 
 addListenerForSingleValueEvent 
 ( 
 new 
  
 ValueEventListener 
 () 
  
 { 
  
 @Override 
  
 public 
  
 void 
  
 onDataChange 
 ( 
 DataSnapshot 
  
 snapshot 
 ) 
  
 { 
  
 // success method is not called 
  
 } 
  
 @Override 
  
 public 
  
 void 
  
 onCancelled 
 ( 
 FirebaseError 
  
 firebaseError 
 ) 
  
 { 
  
 // error callback triggered with PERMISSION_DENIED 
  
 }); 
 }); 
REST
curl https://docs-examples.firebaseio.com/rest/records/
# response returns a PERMISSION_DENIED error

Since the read operation at /records/ is atomic, and there's no read rule that grants access to all of the data under /records/ , this will throw a PERMISSION_DENIED error. If we evaluate this rule in the security simulator in our Firebase console , we can see that the read operation was denied because no read rule allowed access to the /records/ path. However, note that the rule for rec1 was never evaluated because it wasn't in the path we requested. To fetch rec1 , we would need to access it directly:

JavaScript
 var 
  
 db 
  
 = 
  
 firebase 
 . 
 database 
 (); 
 db 
 . 
 ref 
 ( 
 "records/rec1" 
 ). 
 once 
 ( 
 "value" 
 , 
  
 function 
 ( 
 snap 
 ) 
  
 { 
  
 // SUCCESS! 
 }, 
  
 function 
 ( 
 err 
 ) 
  
 { 
  
 // error callback is not called 
 }); 
Objective-C
Note: This Firebase product is not available on the App Clip target.
 FIRDatabaseReference 
  
 * 
 ref 
  
 = 
  
 [[ 
 FIRDatabase 
  
 database 
 ] 
  
 reference 
 ]; 
 [[ 
 ref 
  
 child 
 : 
 @"records/rec1" 
 ] 
  
 observeSingleEventOfType 
 : 
 FEventTypeValue 
  
 withBlock 
 :^ 
 ( 
 FIRDataSnapshot 
  
 * 
 snapshot 
 ) 
  
 { 
  
 // SUCCESS! 
 }]; 
Swift
Note: This Firebase product is not available on the App Clip target.
 var 
  
 ref 
  
 = 
  
 FIRDatabase 
 . 
 database 
 (). 
 reference 
 () 
 ref 
 . 
 child 
 ( 
 "records/rec1" 
 ). 
 observeSingleEventOfType 
 (. 
 Value 
 , 
  
 withBlock 
 : 
  
 { 
  
 snapshot 
  
 in 
  
 // SUCCESS! 
 }) 
Java
 FirebaseDatabase 
  
 database 
  
 = 
  
 FirebaseDatabase 
 . 
 getInstance 
 (); 
 DatabaseReference 
  
 ref 
  
 = 
  
 database 
 . 
 getReference 
 ( 
 "records/rec1" 
 ); 
 ref 
 . 
 addListenerForSingleValueEvent 
 ( 
 new 
  
 ValueEventListener 
 () 
  
 { 
  
 @Override 
  
 public 
  
 void 
  
 onDataChange 
 ( 
 DataSnapshot 
  
 snapshot 
 ) 
  
 { 
  
 // SUCCESS! 
  
 } 
  
 @Override 
  
 public 
  
 void 
  
 onCancelled 
 ( 
 FirebaseError 
  
 firebaseError 
 ) 
  
 { 
  
 // error callback is not called 
  
 } 
 }); 
REST
curl https://docs-examples.firebaseio.com/rest/records/rec1
# SUCCESS!

Overlapping Statements

It's possible for a more than one rule to apply to a node. In the case where multiple rules expressions identify a node, the access method is denied if anyof the conditions is false :

{
  "rules": {
    "messages": {
      // A rule expression that applies to all nodes in the 'messages' node
      "$message": {
        ".read": "true",
        ".write": "true"
      },
      // A second rule expression applying specifically to the 'message1` node
      "message1": {
        ".read": "false",
        ".write": "false"
      }
    }
  }
}

In the example above, reads to the message1 node will be denied because the second rules is always false , even though the first rule is always true .

Next steps

You can deepen your understanding of Firebase Realtime Database Security Rules:

  • Learn the next major concept of the Rules language, dynamic conditions , which let your Rules check user authorization, compare existing and incoming data, validate incoming data, check the structure of queries coming from the client, and more.

  • Review typical security use cases and the Firebase Security Rules definitions that address them .

Design a Mobile Site
View Site in Mobile | Classic
Share by: