Search Lessons, Code Snippets, and Videos
search by algolia
X
#native_cta# #native_desc# Sponsored by #native_company#

Understanding Firebase Database Rules by Example

Episode 11 written by Jeff Delaney
full courses for pro members

Security is a critical concern for any web application. A secure web app must prevent unauthorized database operations, as well as validate the integrity of incoming data.

Firebase allows you to define database security logic in a JSON file that corresponds to the structure of your database.

A NoSQL database is just a series of nodes – and the database.rules.json file allows you to control security and data validation at each node.

You can work with the database rules from an Angular project, or from the Firebase console. Personally, I prefer working from the console because it allows you to easily publish and test your rules.

Three Types of Rules

There are three types of rules you can set - read, write, and validate. Read controls access to data. Write controls creating, editing, or deleting data. Validate controls the format of data.

You can use any combination of these rules together, or none at all.

Special Variables

There are also several built-in variables that give you access to Firebase resources.


  • auth - Gives you access to the user auth state.

  • root - The root of the database and can be traversed with .child('name').

  • data - Data state before an operation

  • newData - Data after an operation (the incoming data)

  • now - Unix epoch timestamp

  • ${child key} - Wildcard for any child key at a database node.

Common Firebase Database Rules and Scenarios

Let’s run through the most common security scenarios you might run into.

No security. Anybody can read or write

"rules": {
".read": true,
".write": true
}

Full security. Nobody can read or write

"rules": {
".read": false,
".write": false
}

Only authenticated users can read and write

"rules": {
".read": "auth != null",
".write": "auth != null"
}

Users can only write data they own

Let’s assume you have an app that has user comments nested under their user id. You can prevent other users from accessing or writing this data by comparing the auth.uid to the uid database key.

"rules": {
"posts": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
}
}
}

Only authorized moderator users can write to the database

Let’s imagine you only allow trusted users who have been promoted to moderators to modify data. You will first need to save an attribute in the database, then check that the authorized user has this attribute. The root variable allows you to traverse the database to verify that the moderator attribute is true.

"rules": {
"posts": {
"$uid": {
".write": "root.child('moderators').child(auth.uid).val() === true"
}
}
}

You want to validate input meets a certain criteria

You don’t want users to just post anything to your database. Let’s validate a post is at least 1 character, limited to 140 character long, and must be a string value. You can string together validators with logical operators.

SEE Gist Below

Ensure certain child attributes are present

Let’s say new posts must have username and a timestamp.


"rules": {
"posts": {
"$uid": {
".validate": "newData.hasChildren(['username', 'timestamp'])"
}
}
}


### Validate that a timestamp is not a future value

The now variable can be used to check the input timestamp against the current time in Firebase.

SEE Gist Below

Ensuring that data does (or does not) exist before writing

By checking if data or newData exists, you can control whether a user can perform create, update, or delete operations.

SEE Gist Below

Common Pitfall - Cascading Rules

On a final note, let’s look at a common pitfall with firebase security. You cannot grant access to data, then revoke it later. However, you can do the opposite - revoke access, then grant it back later. That being said, it is usually best to deny access by default, then grant access when the ideal conditions have been satisfied deeper in the tree.

The second rule in the tree will not revoke the access already granted The second rule in the tree will not revoke the access already granted

You might think this write should be denied, but it will be allowed because the first rule defined takes priority. You would want to refactor your rules to prevent access by default.

Its best to lock sensitive data by default

That’s it for database rules. You should be able to use a combination of these principles for virtually any database security configuration.