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. |
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.
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.
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:
{
"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
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
Also Check:
This post was last modified on December 11, 2023 5:47 pm