Firebase add users but disable any modification - firebase

got stuck on this problem: I want to allow user to register on my webpage using firebase, for that reason I planned the following structure:
users : {
user0 : { //user specific data },
user1 : { //... }
...
}
Everything works fine (writing, reading...) until I change the security rules. I want to change them because I want the users only to register and not to have the power to delete their or potentially other user accounts. But I couldn't find anything very helpful on that. Below is what I'm currently left with:
{
"rules": {
"users" : {
".read": true,
"$uid" : {
".write": "auth !== null && auth.uid === $uid"
}
}
}
}
I'm wondering how to set up the rules such that users can only add new accounts.
I would appreciate any help!
Thaanks :)
Edit: Here's what I wanted to achieve with the rules above, maybe the below example using the simulator will make my point clear.
The first thing, I want to do is, is to let a user register at the /users node. Therefore, I chose the Custom Auth point in the simulator:
{ provider: 'anonymous', uid: '002a448c-30c0-4b87-a16b-f70dfebe3386' }.
Now, if I choose "simulate write" and give the path /users and the following key-value pair:
{ "002a448c-30c0-4b87-a16b-f70dfebe3386": { "1" : {"a" : "b"}}}
I get the below message (see Result_2), which tells me, that I cannot write to /users because there's no write rule allowing it, which I totally understand in the above security rules configuration, but I don't know how to change them such that I am able to write key-value pairs as the above one while still allowing each user to write on there entry only. I.e. the user with the uid: 002a448c-30c0-4b87-a16b-f70dfebe3386 would be allowed to write on the corresponding value with the rules above as long as he is authenticated (see Result_1).
E.g. Custon Auth authenticated user writing ON HIS ENTRY: (WORKS PERFECTLY AS EXPECTED)
{ provider: 'anonymous', uid: '002a448c-30c0-4b87-a16b-f70dfebe3386' }.
As the previous time. Now, "simulate write" on path:
/users/002a448c-30c0-4b87-a16b-f70dfebe3386
Result_1:
Attempt to write {"4":{"name":"fred"}} to /users/002a448c-30c0-4b87-a16b-f70dfebe3386 with auth={"provider":"anonymous","uid":"002a448c-30c0-4b87-a16b-f70dfebe3386"}
/
/users
/users/002a448c-30c0-4b87-a16b-f70dfebe3386:.write: "auth !== null && auth.uid === $uid" => true
Write was allowed.
Result_2: Writing the user onto the /users nodes fails, i.e. no registering is possible. And I want here to be able to add a user to /users but not be able to modify/delete user from /users. See simulator output below.
Attempt to write {"002a448c-30c0-4b87-a16b-f70dfebe3386":{"1":{"a":"b"}}} to /users with auth={"provider":"anonymous","uid":"002a448c-30c0-4b87-a16b-f70dfebe3386"}
/
/users No .write rule allowed the operation.
Write was denied.

Permissions cascades - once you give a user a permission on /users you cannot remove that permission on /users/$uid anymore.
The solution is to only grant permission on the lowest level, so in your case:
{
"rules": {
"users" : {
".read": true,
"$uid" : {
".write": "auth !== null && auth.uid === $uid"
}
}
}
}

Related

Firebase rules warning - any user can read your entire database [duplicate]

I recently received an email from firebase telling me that my realtime database has insecure rules. These are the rules that I have set:
{
"rules": {
".read": "auth != null",
".write": "auth != null"
}
}
Is this not a secure rule?
Email/Password is the only sign-in method that I have enabled.
firebaser here
I'm sorry if the email wasn't very explicit about what isn't secure about those rules. Securing your user's data is a crucial step for any app that you make available, so I'll try to explain a bit more about how that works below.
The (default) rules you have allow anyone who is signed in to your back-end full read/write access to the entire database. This is only a very basic layer of security.
On the one hand this is more secure than just granting everyone access to your database, at least they have to be signed in.
On the other hand, if you enable any auth provider in Firebase Authentication, anyone can sign in to your back-end, even without using your app. Depending on the provider, this can be as easy as running a bit of JavaScript in your browser's developer console. And once they are signed in, they can read and write anything in your database. This means they can delete all data with a simple command like firebase.database().ref().delete().
To make the data access more secure, you'll want to more tightly control what each signed-in user can do. For example, say that you keep a profile with information about each user under /users. You might want to allow all users to access these profiles, but you definitely want users to only be allowed to modify their own data. You can secure this with these rules:
{
"rules": {
"users": {
".read": true,
"$user_id": {
// grants write access to the owner of this user account
// whose uid must exactly match the key ($user_id)
".write": "$user_id === auth.uid"
}
}
}
}
With these rules, everyone (even non-authenticated users) can read all profiles. But each profile can only be modified by the user whose profile it is. For more on this, see the Firebase documentation on securing user data.
In addition to ensuring that all access to data is authorized, you'll also want to ensure that all data stored is valid to whatever rules you have for you app. For example, say that you want to store two properties for a user: their name, and their age (just for the sake of the example, in reality you'd probably store their date-of-birth instead). So you could store this as something like:
"users": {
"uidOfPuf": {
"name": "Frank van Puffelen",
"age": 48
}
}
To ensure only this data can be written, you can use this rules:
{
"rules": {
"users": {
".read": true,
"$user_id": {
".write": "$user_id === auth.uid",
".validate": "data.hasChildren('name', 'age')",
"name": {
".validate": "data.isString()",
},
"age: {
".validate": "data.isNumber()",
},
"$other: {
".validate": false
}
}
}
}
}
These rules ensure that each user profile has a name and age property with a string and numeric value respectively. If someone tries to write any additional properties, the write is rejected.
Above is a quick primer on how to think about securing your (user's) data. I recommend that you check out the Firebase security documentation (and the embedded video) for more.
Update: since May 2021 you can also use Firebase App Check to restrict access to calls just coming from your web site or app. This is another, quick way to reduce the abuse of your database. This approach is not foolproof though, so you'll want to combine App Check for broad protected, with the security rules for fine-grained control.
You can also mute alerts by visiting the link at the bottom of the email.
https://console.firebase.google.com/subscriptions/project/<YOUR_PROJECT_NAME>
I changed rules
{
"rules": {
"users": {
".read": true,
"$user_id": {
// grants write access to the owner of this user account
// whose uid must exactly match the key ($user_id)
".write": "$user_id === auth.uid"
}
}
}
}
But after that page not working.

firebase rule to make sure a owner of comment has access to read, write and any other user to only like it. number_check is the user table

The following is the database structure and i want to make sure the owner of the comment whose user_id is part of the comment object to have read and write access to the comment and all other users have read access to comment and the ability to like it to increase like_count:
The following is the security rule I came up with:
{
"rules": {
"comments": {
".read": "root.child('users').child(auth.uid).val() != null",
".write": "(newData.parent().child('users').child(auth.uid).val() != null && newData.parent().child('comments').hasChildren().hasChildren().child('user_id').val() == auth.uid)",
"$commentId": {
".read": "root.child('users').child(auth.uid).val() != null",
".write": "(newData.parent().parent().child('users').child(auth.uid).val() != null && !newData.parent().parent().child('comments').hasChildren().hasChildren().child('like_count'))"
}
}
}
}
So, for read better will be:
".read": "root.child('users').hasChild(auth.uid)"
".write" is more complicated:
You need to only allow edit in like_count, and you need to allow for only one like per user
Proper way to do this will be expand structure of your "post", something like like_list, here or in other place to protect data transfer from growing when post will be download by client
So in "comment" node you want to allow every auth user to .write and .read, they will be able to add new comment then (upper rules will be fine for this).
For $commentId node rules will look like:
".write":".data.child('user_id').val() == auth.uid"
You can link like_list to a clinet by adding user_id to the "like / $ commentId" node and setting listener in firebase funtions for this node. adding new user_id will fire "write" event and then call function to secure increase the value of like_count.
You can archive it with a firebase-function and transaction.
https://www.tutorialspoint.com/firebase/firebase_write_transactional_data.htm
If you really don`t want to change schema then you need to secure every single child of comment, like:
"$commentID":{
"comment":{ ".write":"".data.parent().child('user_id').val() == auth.uid" ),
...//things allowed to edit by owner
"like_count":{
".write":"root.child('users').hasChild(auth.uid)",
".validate":"newData.val() == data.val() + 1"
}
}
But this will not secure post from giving more than one like per user.

Do my Firebase rules expose each users private information?

The user needs to be able to load a list of the other users, which is why I have set the "read" to true. They should only be able to write within their own folder.
My question is, does this expose all of the users information to eachother? If a user has sensitive in one of their folders information, can this be stolen?
{
"rules": {
"users": {
".read": true,
"$userId": {
".write": "$userId === auth.uid"
}
}
}
}
As you said, the ".read": true will give every user access to all the users data, since this rule is saying that we can read everything from the users node in your database.
Your rule only allows the current logged in user to write on its own node, but others users can't.

Firebase Auth with Firestore [duplicate]

I recently received an email from firebase telling me that my realtime database has insecure rules. These are the rules that I have set:
{
"rules": {
".read": "auth != null",
".write": "auth != null"
}
}
Is this not a secure rule?
Email/Password is the only sign-in method that I have enabled.
firebaser here
I'm sorry if the email wasn't very explicit about what isn't secure about those rules. Securing your user's data is a crucial step for any app that you make available, so I'll try to explain a bit more about how that works below.
The (default) rules you have allow anyone who is signed in to your back-end full read/write access to the entire database. This is only a very basic layer of security.
On the one hand this is more secure than just granting everyone access to your database, at least they have to be signed in.
On the other hand, if you enable any auth provider in Firebase Authentication, anyone can sign in to your back-end, even without using your app. Depending on the provider, this can be as easy as running a bit of JavaScript in your browser's developer console. And once they are signed in, they can read and write anything in your database. This means they can delete all data with a simple command like firebase.database().ref().delete().
To make the data access more secure, you'll want to more tightly control what each signed-in user can do. For example, say that you keep a profile with information about each user under /users. You might want to allow all users to access these profiles, but you definitely want users to only be allowed to modify their own data. You can secure this with these rules:
{
"rules": {
"users": {
".read": true,
"$user_id": {
// grants write access to the owner of this user account
// whose uid must exactly match the key ($user_id)
".write": "$user_id === auth.uid"
}
}
}
}
With these rules, everyone (even non-authenticated users) can read all profiles. But each profile can only be modified by the user whose profile it is. For more on this, see the Firebase documentation on securing user data.
In addition to ensuring that all access to data is authorized, you'll also want to ensure that all data stored is valid to whatever rules you have for you app. For example, say that you want to store two properties for a user: their name, and their age (just for the sake of the example, in reality you'd probably store their date-of-birth instead). So you could store this as something like:
"users": {
"uidOfPuf": {
"name": "Frank van Puffelen",
"age": 48
}
}
To ensure only this data can be written, you can use this rules:
{
"rules": {
"users": {
".read": true,
"$user_id": {
".write": "$user_id === auth.uid",
".validate": "data.hasChildren('name', 'age')",
"name": {
".validate": "data.isString()",
},
"age: {
".validate": "data.isNumber()",
},
"$other: {
".validate": false
}
}
}
}
}
These rules ensure that each user profile has a name and age property with a string and numeric value respectively. If someone tries to write any additional properties, the write is rejected.
Above is a quick primer on how to think about securing your (user's) data. I recommend that you check out the Firebase security documentation (and the embedded video) for more.
Update: since May 2021 you can also use Firebase App Check to restrict access to calls just coming from your web site or app. This is another, quick way to reduce the abuse of your database. This approach is not foolproof though, so you'll want to combine App Check for broad protected, with the security rules for fine-grained control.
You can also mute alerts by visiting the link at the bottom of the email.
https://console.firebase.google.com/subscriptions/project/<YOUR_PROJECT_NAME>
I changed rules
{
"rules": {
"users": {
".read": true,
"$user_id": {
// grants write access to the owner of this user account
// whose uid must exactly match the key ($user_id)
".write": "$user_id === auth.uid"
}
}
}
}
But after that page not working.

Firebase Rules for object only for creator and admin user

I am really confused with Firebase rules and need some help. I googled a lot but actually got only more confused and still don't get it work.
Lets say I have a Firebase db object like this:
root/orders/$id
and inside the $id, I have a child user_id:
root/orders/$id/user_id
Now I want a rule which only allow the user to READ his own orders (but not write anymore in existing once, how ever he need to create new once) and additional I want the users which are admins to READ/WRITE all orders at any time.
I come up with this so far
"orders": {
"$id": {
".read": "root.child('orders').child($id).child('user_id').val() == auth.uid || root.child('users').child(auth.uid).child('admin').val() == 'user_is_admin'",
".write": "root.child('users').child(auth.uid).child('admin').val() == 'user_is_admin'"
},
".write": "root.child('users').child(auth.uid).child('admin').val() == 'user_is_admin'",
".read": "root.child('users').child(auth.uid).child('admin').val() == 'user_is_admin'",
".indexOn": ["status", "user_id"]
},
My admins are marked as admins in my user table:
root/users/$id/admin (== user_is_admin)
My intention was for the first part to allow users with the same auth.uid as the requested /orders/$id/user_id to read their orders and for admins to read and write. The admin part is working, but my user has no access for some reason.
The second part was for admins to have read/write access to all orders (without specific $id) which also works fine plus a normal user need the write to CREATE a new order here.
To resume the admin part of my rules works, but the user part does not.
1. my user cant read is own orders
2. my user cant create a new order
I would be really happy if somebody can help me out here.
The Rules:
The following rules should enforce the policies you've outlined in your question:
"orders": {
"$id": {
".read": "data.child('user_id').val() === auth.uid",
".write": "!data.exists() && newData.child('user_id').val() === auth.uid"
},
".write": "root.child('users').child(auth.uid).child('admin').val() === 'user_is_admin'",
".read": "root.child('users').child(auth.uid).child('admin').val() === 'user_is_admin'",
".indexOn": ["status", "user_id"]
}
Note that Firebase security rules cascade, so once you've granted read/write permissions to admins for orders, you don't need to repeat the rules for orders/$id. (Something to remember - although it's not an issue with your rules - is that once you grant a permission on a parent you cannot revoke it on a child.)
The orders/$id/.read rule uses data.child to compare the user_id and the auth.uid. This is the same as your rule, it's just a little shorter and does not include the admin check (which cascades).
In addition to checking that newData (the value being written) contains the user_id, the orders/$id/.write rule checks to see that data (the previous value) does not exist. That will allow creates, but will deny updates and deletes.
Orders for Users:
As noted in your comment, it's not possible for a user to query a list of orders under the orders key, as the user won't have permission to read the orders of other users (they'd need to be readable to be filtered out). You could solve the problem by storing a mapping of orders by user, like this:
"orders": {
"order_1": {
"user_id": "user_1",
...
},
"order_2": {
"user_id": "user_1",
...
},
"order_3": {
"user_id": "user_2",
...
}
},
"ordersByUser": {
"user_1": {
"order_1": true,
"order_2": true
},
"user_2": {
"order_3": true
}
}
You can use Firebase's multi-location updates to make maintaining the mapping less tedious. For example, to add another order for user_1, you could do this:
firebase.database().ref().update({
"orders/order_4": {
"user_id": "user_1",
...
},
"ordersByUser/user_1/order_4": true
});
This would let users obtain a list of order IDs (and, from those, the orders themselves) and admin users could still obtain a list of all orders, etc.
Also, you should include a rule so that users can only read their own order IDs under ordersByUser, etc.

Resources