Firebase Database Security – Securing Your Firebase RealTime Database?

By Saurabh Barot   |   31 January, 2022
Firebase Database Security – Securing Your Firebase RealTime Database?

Quick Summary:

You can control database access with Firebase Realtime Database Security Rules. Using the Flexible rules syntax, you can design rules that match anything, from all writes to your database to operations on specific nodes. Declarative Firebase Realtime Database Security Rules are separately defined from the product logic. This has multiple advantages: clients are not responsible for maintaining security, data is not compromised by defective implementation, and middleware such as servers may not be required to secure data from the actual world.

Who has read and write access to your database, how your data is formatted, and what indexes exist are all determined by Firebase Database Security Rules? These rules are always in effect on the Firebase servers and are automatically enforced. Every read and write request will be fulfilled only if your rules permit it. Your rules prevent anyone from accessing your database by default. This is to prevent exploiting your database until you have time to adjust your rules or set up authentication.

There are four types of Firebase Realtime Database Security Rules, each with its Javascript syntax:

Rules Type Description
.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 data types.
.indexOn Specific a child to the index to support ordering and querying.

Four types of Firebase Realtime Database Security Rules

Why Security Matters in Firebase?

Firebase is a cloud database storage service that can be accessed from any connected device. You must write security rules to secure your data because any client can connect to any firebase. If not managed appropriately, you will be vulnerable to a variety of attacks and weaknesses.

Firebase Security rules check a pattern against database paths and then apply custom conditions to grant access to data at those paths. Rules allow access to the data at that path if multiple rules match a path and any matched criteria grant access. Firebase is GDPR and CCPA compliant, as well as having Data Processing and Security Terms. It is also certified to important privacy and security standards, including ISO and SOC compliance. Despite this, we are constantly left with the question of how secure is firebase authentication? Is firebase secure?

There are various vulnerabilities in Firebase that can be remedied by following the right procedures. For a better understanding, consider the following example.

Name: Exposure of Sensitive Information to an Unauthorized Actor

Severity: Medium

Classification: CWE-200

Description:  Firebase is a mobile and online app development platform created by Firebase Inc, later acquired by Google in 2014. As a REST endpoint, you can access any Firebase RealTime Database URL. We may get the data by appending.JSON to the end of the URL and sending a request from an HTTPS client.

This Firebase Realtime Database URL is proven to be accessible without authentication. If the database contains sensitive information, it’s a good idea to limit who has access to it.

The firebase real-time database URL that is accessible without authentication can be mitigated. If the database contains sensitive information, it’s a good idea to limit who has access to it.

Getting Started:  Firebase Security Rules Checklist

Firebase Security Rules give powerful, entirely tailored protection for your data in Cloud Firestone, Realtime Database, and Cloud Storage. Following the steps in this guide, you can simply get started with rules, encrypting your data, and protecting your app from fraudulent users.

Firebase Security Rules Checklist

1. Understand the Firebase Security Rules Language

Take some time to review the unique Firebase Security Rules language for the Firebase products you’re using before you start writing rules. For its rules, Realtime Database uses a JavaScript-like syntax and a JSON structure.

2. SetUp Authentication

Add Firebase Authenticate to your app if you haven’t already. Firebase Authentication interfaces with Firebase Security Rules to give full verification capabilities. It supports several standard authentication methods. For your app, you can provide additional, specific authentication information.

3. Define your Data & Rules Structures

The way you organize and implement your rules may be influenced by how you structure and implement your data. Consider the ramifications of your data structures on your rule structure as you define them. For example, you might wish to include a field in your real-time database that shows each user’s role. Then your rules may read that field and provide access based on roles.

As you define your data and rules architectures, keep in mind how rules cascade or don’t cascade, depending on your product. With a Firebase RealTime database, rules work from top-down, with shallower rules overriding deeper rules. If a grant reads or writes permission at a particular path, it also grants access to all child nodes. In contrast, with Cloud Firestone and cloud storage, rules apply only at specific data hierarchy levels, and you write the rules to control access on different levels.

4. Access your rules

Use the Firebase CLI or the Firebase console to see your existing Rules. To avoid accidentally overwriting updates, make sure you alter your rules with identical techniques every time. If you’re unsure whether your locally created rules are up to date, the Firebase interface always displays the most recent version of your Firebase Security Rules.

Select your project in the Firebase console, then click Realtime Database in the left-hand navigation panel to access your rules. Once you’ve found the right database or storage bucket, click Rules.

Go to the rules file listed in your firebase.json file to access your rules via the Firebase CLI.

5. Write basic rules

As you’re developing your app and understanding Rules, try implementing Rules to address a few basic uses, cases including the following:

  • Content-owner only: Restrict access to content by the user.
  • Mixed access: Restrict write access by the user but allow public read access.
  • Attribute-based access: Restrict access to a group or type of user.

6. Test your rules

You may use the Firebase Rules Playground to quickly validate the behaviour of your Firebase Realtime database Security Rules as you’re setting them up in the Firebase interface. However, before deploying any modifications to production, we recommend conducting more comprehensive testing using the local Emulator suite.

7. Deploy your rules

To deploy your rules to production, use the Firebase console or the Firebase CLI. Deploy the sources to production once you’ve changed and tested your rules. Use the instructions below to selectively deploy them independently or as part of your regular deployment procedure.

// Deploy your .rules file
firebase deploy --only firestore: rules

Factors affecting Firebase Database Security

The Firebase Realtime Database comes with a comprehensive collection of tools for controlling your app’s security. Authenticating your users, enforcing user rights, and validating inputs are all made simple with these tools. Apps built with Firebase have more client-side code than apps built with a different technology stack. As a result, our approach to security may differ from what you’re used to.

Tip: Many more security concerns are handled by the Firebase Realtime Database. For example, we encrypt our certificates with 2048-bit keys and apply best practices for authentication tokens.

Factors affecting Firebase Database Security

Authentication

Identifying your app’s users is a common initial step in safeguarding it, and authentication is the term for this procedure. Users can sign in to your app using Firebase Authentication. Drop-in support for popular authentication methods like Google and Facebook, email and password login, anonymous login, and more are included in Firebase Authentication.

For securing firebase, the concept of user identity is crucial. Different users have distinct data and, on occasion, different skills. In a chat program, for example, each message is tagged with the user who sent it. Users may be able to delete their messages, but not those posted by others.

Authorization

Only part of security is identifying your user. Once you’ve figured out who they are, you’ll need a mechanism to limit their access to your database’s contents. You may control access for each user using Firebase Database Security Rules. Here’s an example of a set of security rules that allows anyone to view but not write to the route /Test/:


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

This ruleset gives read access to any data at path /Test/and any deeper paths such as /Test/bar/baz because. read and. write rules cascade. Shallower rules in the database override deeper rules, so read access to /Test/bar/baz would still be given even if a rule at the path /Test/bar/baz is evaluated to false in this case.

Built-in variables and functions in the Firebase Realtime Database Security Rules allow you to refer to other pathways, server-side timestamps, authentication information, and more. Here’s an example of a rule that gives authorized users write access to /users/, where is the user’s ID retrieved via Firebase Authentication.

{
  "rules": {
    "users": {
      "$uid": {
        ".write": "$uid === auth.uid"
      }
    }
  }
}

Data Validation

The Firebase Realtime Database doesn’t have a schema. This makes it simple to alter things while you work, but data consistency is critical once your product is ready for distribution. The. Validate rule in the rules language allows you to apply validation logic using the same expressions as the. Read and. Write rules. The only difference is that validation rules do not cascade. Therefore for the write to be allowed, all relevant validation rules must evaluate to be true.

Data written to /Test/ must be a string of fewer than 100 characters, according to these rules:

{
  "rules": {
    "Test": {
      ".validate": "newData.isString() && newData.val().length < 100"
    }
  }
}

Validation rules have the same access to built-in functions and variables as. Read and. Write rules. These can be used to define validation rules that take into account data from other tables in your database, your user’s identification, server time, and more.

Note: The .validate rules are only evaluated for nonnull values and do not cascade.

Defining Database Index

Data can be ordered and queried using the Firebase Realtime Database. Because the database provides ad hoc querying for tiny data sets, indexes are rarely needed during development. However, when you start your app, you should declare indexes for any queries you have so that they continue to work as your app expands.

The .indexOn rule is used to specify indexes. Here’s an example index declaration for a list of dinosaurs that would index the height and length fields:

{
  "rules": {
    "dinosaurs": {
      ".indexOn": ["height", "length"]
    }
  }
}

build a Featured-Rich Secure App With FireBase?

Avail Free Firebase Consultation from Experts

Firebase Database Security Rules

Security Rules for real-time databases are composed of JavaScript-like phrases enclosed in a JSON Document. Your rules should be organized in the same way that the data in your database is organized.

A set of nodes to be protected, the access methods involved, and the criteria under which access is either allowed or denied are all identified by basic rules. The fundamental syntax is as follows:

Firebase Database Security Rules

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

Let’s take a look at the security rules for the Firebase database, which were created in 2011 and are specific to the JSON data model.

Let’s take a look at the best practices that will assist you with securing firebase.

Follow the JSON

JSON is used to store data in a real-time database. Its security rules are written in JSON, and they follow the pattern of the data you intend to store.

The basic JSON rules object is as follows:

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

There’s a root attribute called rules, and there are two permission types. Read and. Write. There’s also an auth object that we may test to make sure it’s not null in the rule conditions. If it isn’t null, the request must have originated with the authenticated user.

Let’s now safeguard the user’s node in our fictitious JSON tree.
Data


{ "users": { 
	"userOne": {...}, 
	"userTwo": {...} 
	} 
}

Rules

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

Take note of how we established a new node under rules and named it users. Yes, our rules are nested to fit our data. So, in JSON, users match our user’s node.

Wildcards

In Firebase real-time database security rules, wildcards are an important topic. Here’s a $user id wildcard example:

{ 
	"rules": { 
	"users": { 
		"$userId": { 
// grants write access to the owner of this user account 
// whose uid must exactly match the key ($user_id)
 ".write": "$userId === auth.uid" 
			} 
		} 
	} 
}
}

The dollar symbol specifies that $user id is a wildcard. You can give wildcards whatever name you choose. You might have called it $Knox, but then your condition statements would have to refer to the wildcard as Knox.

Wildcard applies to all properties that aren’t otherwise specified. Assume the following information:

{
  "users": {
    "userOne": {...},
    "userTwo": {...},
    "userThree": {...},
  }
}

There are three of us. Let’s pretend that all user data is public except for admin’s, allowing userTwo to write to everyone’s record while also reading and writing to her own.

{
  "rules": {
    "users": {
      "$userId": {
        ".read": true,
        ".write": "auth.uid === 'userThree'"
      },
      "userThree": {
        ".read": "auth.uid === 'userThree'",
        ".write": "auth.uid === 'userThree'"
      }
    }
  }
}

To establish rules for all users, we use the $user id wildcard. Then there are certain rules. Users. UserTwo.

Cascading Rules

When creating your data models, keep in mind that Firebase Realtime Database security policies can cascade. Let’s tweak the previous example a little and see if we can prevent all users from reading other users. $userId.superSecretUserAttributes.

{
  "rules": {
    "users": {
      "$userId": {
        ".read": true,
        ".write": "auth.uid === 'userThree'",
        "superSecretUserAttributes": {
          ".read": "auth.uid === 'userThree'"
        }
      }
    }
  }
}

Remember how rules build on each other? We were unable to secure superSecretUserAttributes since we were granted. Higher up the chain, read access is granted. All users can still read superSecretUserAttributes and the nested in this example, and the read rule is disregarded.

Consider cascading rules when designing your data.

Cascading security rules imply that you should never need to stack security rules in your data structure. Here’s how we like to write rules:

{
  "rules": {
    "users": {
      "$uid": {
        ".read": "auth.uid === $uid || auth.token.admin === true",
        ".write": "auth.uid === $uid || auth.token.admin === true"
      }
    },
    "userOwned": {
      "$objectType": {
        "$uid": {
          ".read": "auth.uid === $uid || auth.token.admin === true",
          ".write": "auth.uid === $uid || auth.token.admin === true"
        }
      }
    },
    "userReadable": {
      "$objectType": {
        "$uid": {
          ".read": "auth.uid === $uid || auth.token.admin === true",
          ".write": "auth.token.admin === true"
        }
      }
    },
    "userWriteable": {
      "$objectType": {
        "$uid": {
          ".read": "auth.token.admin === true",
          ".write": "auth.uid === $uid || auth.token.admin === true"
        }
      }
    },
    "adminOwned": {
      "$objectType": {
        "$uid": {
          ".read": "auth.token.admin === true",
          ".write": "auth.token.admin === true"
        }
      }
    },
    "public": {
      "$objectType": {
        "$uid": {
          ".read": true,
          ".write": "auth.token.admin === true"
        }
      }
    }
  }
}

That’s all there is to it! In our data structure, there are six base nodes.

{
  "users": {...},
  "userOwned": {...},
  "userReadable": {...},
  "userWriteable": {...},
  "adminOwned": {...},
  "public": {...}
}

And have you seen how each base node has a wildcard $objectType nested beneath it? This allows us to save various types of objects, all of which will inherit their parent node’s rules.

Rather than battling cascading rules, we’re making use of them and drastically decreasing the number of rules we create.

Employing auth. token. admin === true, and we’re using custom claims to allow users with the admin claim to view and write everything. Custom claims are great because they work with all three types of security rules—Firestore, RTDB, and Storage—as well as the current user JWT in your browser.

Validation

To be honest, we do most of our validation in our client-side applications; however, the RTDB’s.validate security rule allows you to validate your data layer directly.

If you require validation, we recommend thoroughly reading the reference documents. We also advise against going overboard with validation in security rules. These rules should be viewed as a flexible technique to improve the security of your app if you have sensitive data or actions that require greater confidence than what the. Read and. Write rules can provide. It’s time to implement some. validate rules.

We’re sure we could use the security rules to create a comprehensive validation layer, but we don’t. Because most of our writers are unlikely to be misused by an attacker, we use client-side validation >99% of the time. Client-side validation won’t prevent capable hackers from connecting directly to your database and testing your endpoints; but, it’s easy enough to hide anything worth hacking deep within a cloud function or an admin-only data node, so try that first before relying on validation rules.

Preventing Duplicates

Step 1: Normalize the data structure.

The first step is to change the root path such that it includes a top-level node. As a result, when a new user is created, we will add the user’s phone number to this node if the validation is successful. The following is a diagram of our new data structure:

{
  "users" : {
    "user1" : {
      "firstName" : "Amelia",
      "lastName" : "Clark",
      "age": 24,
      "phoneNumber" : "07012345678"
    },
    "user2" : {
      "firstName" : "Glen",
      "lastName" : "Michelle",
      "age": 12,
      "phoneNumber" : "06034345453"
    },
    "user3" : {
      "firstName" : "Knox",
      "lastName" : "West",
      "age": 29,
      "phoneNumber" : "09034564543"
    },
    ...
  },
  "phoneNumbers" : {
    "07012345678": "user1",
    "06034345453": "user2",
    "09034564543": "user3",
    ...
  }
}

Step 2: Enforce New Data Structures

Modify the new data structures as follows:

{
  "rules": {
    "users": {
      "$uid": {
        ...
        "phoneNumber": { 
          ".validate": "newData.isString() && 
            newData.val().length == 11 &&
            !root.child('phoneNumbers').child(newData.val()).exists()"
        },
      }
    }
  }
}

We’re checking if the phone number is unique by seeing if it’s already been registered by the user. If not, the validation was successful, and the write operation was accepted; if not, it was rejected.

10 common Firebase Database Security Rules Templates

Firebase Database Security Rules Templates

1. No security

If you set.write= true and.read= true, anyone can write and read your database, even if they aren’t a user of your application. You can establish public rules throughout the construction of the application so that you can easily write and read your database. Remember that you should never use Any Security Rules in production; otherwise, anyone can access your sensitive data. It’s great for prototyping, especially if authentication isn’t a must for your project.

// No Security
{
 “rules”: {
 “.read”: true,
 “.write”: true
 }
}

2. Full Security

By default, these rules are available. You won’t write to or read from your database if you use Full Security Rules. You can access your database through the Firebase Console Dashboard if you’re adding these rules.

// Full security
{
 “rules”: {
 “.read”: false,
 “.write”: false
 }
}

3. Just Authenticated Users can Write Data

// Only authenticated users can access/write data
{
 “rules”: {
 “.read”: “auth != null”,
 “.write”: “auth != null”
 }
}

4.  Authenticate User from a Special Domain

This rule comes in handy when you want to authenticate your users only if they’re registered from a specific domain.

// Only authenticated users from a particular domain (example.com) can access/write data
{
 “rules”: {
 “.read”: “auth.token.email.endsWith(‘@example.com’)”,
 “.write”: “auth.token.email.endsWith(‘@example.com’)”
 }
}

5. User Data Only

Firebase authenticates the access granted by users. $uid is the unique ID of every Firebase authenticated user, as demonstrated in the code below, and $uid also symbolizes a wildcard. The Firebase database provides a wildcard path for describing dynamic child keys and IDs.

// These rules grant access to a node matching the authenticated
// user's ID from the Firebase auth token
{
  "rules": {
    "users": {
      "$uid": {
        ".read": "$uid === auth.uid",
        ".write": "$uid === auth.uid"
      }
    }
  }
}

6. Just validate User from the Different Location in the Database

You can also validate a user from a specific database location. “users” is a child node given anywhere in the database that contains a child node of “moderator” in the example below. You can validate if the “moderator” node value equals “true.”

// Validates user is moderator from the different database location
{
 “rules”: {
 “posts”: {
 “$uid”: {
 “.write”: “root.child(‘users’).child(‘moderator’).val() === true”
 }
 }
 }
}

7.  Validate String Length and Datatype

You can also check the length and datatype of any String. I gave three rules in the example below. The first is “newData.isString(),” which indicates that the data type is a string. The second string value for “newData.val().length > 0” must not be null. The third-string value for “newData.val().length = 140” must be fewer than 141 characters.

// Validates string datatype and length range
{
 “rules”: {
 “posts”: {
 “$uid”: {
 “.validate”: “newData.isString() 
 && newData.val().length > 0
 && newData.val().length <= 140”
 }
 }
 }
}

8.  Check Child Attribute Presence

You may also see if a particular child node is present in the database. I specified an array that contains the child nodes in the database in the example “[‘username’, ‘timestamp’].”

// Checks presence of child attributes
{
 “rules”: {
 “posts”: {
 “$uid”: {
 “.validate”: “newData.hasChildren([‘username’, ‘timestamp’])”
 }
 }
 }
}

9. Validate the TimeStamp

“newData.val() = now” in the example below. You can verify data that has been entered recently or in the past. The currently available time in Milliseconds is represented by “now.”

// Validates timestamp is not a future value
{
 “rules”: {
 “posts”: {
 “$uid”: {
 “timestamp”: { 
 “.validate”: “newData.val() <= now” 
 }
 }
 }
 }
}

10. Preventing Deletion and Updations.

In the example below, “!data. exists()” allows you to write data to the database even if it isn’t already there. You can’t delete or change info once it’s been entered.

// Prevents Delete or Update
{
 “rules”: {
 “posts”: {
 “$uid”: {
 “.write”: “!data.exists()”
 }
 }
 }
}

Conclusion

This blog discusses the best ways to integrate the best firebase realtime database security for your custom firebase project. In this tutorial, you have learned about Firebase Database Security rules, how to prevent unauthorized access, prevent duplicates, and make sure that the data is structured.  If you want to have in-depth knowledge of Firebase Database Security Rules, please refer to the official documentation.

“Are you looking to hire LARAVEL Developers?”

Find the best full-stack Laravel Developers in India to start your web development project within 48 hours

Get in Touch!

    Saurabh Barot

    Saurabh Barot is co-founder and Experienced Chief Technology Officer at Aglowid IT Solutions. His deep industry experience in Angular, React, Ruby on Rails, PHP, Mean Stack and just to name few, has helped the company reach to a new level. He believes that technology is all about exploring new possibilities. Whatever a mind can think (exception can't be ruled out), technology brings it into reality.

    Related Posts