Firebase Realtime Database Insecure Rule Warning - firebase

I keep getting emails saying my database is not secure after implementing my security rules. The emails specify that any authenticated user can read/write to my database but I implemented specific access rules:
{
"rules": {
"posts": {
".read": "auth.uid !== null",
".write": "auth.uid !== null && newData.hasChildren(['score', 'quote',
'description', 'source', 'sourceType', 'ownerID', 'ownerImageURl', 'ownerUsername', 'timestamp', 'usersVoted'])",
".indexOn":["sourceType", "ownerID"],
"$postID": {
".write": "!data.hasChild('ownerID')",
"score": {
".write": "newData.isNumber() && (newData.val() === data.val() + 1 || newData.val() === data.val() - 1) && !root.child('posts').child('$postID').child('usersVoted').hasChild(auth.uid)"
},
"usersVoted": {
".write": "!data.hasChild(auth.uid)",
"$userID": {
".write": false
}
}
}
},
"users": {
".write":"auth.uid !== null && !data.hasChild(auth.uid)",
"$userID": {
".read": "auth.uid === $userID",
".write": "auth.uid === $userID"
}
},
"comments": {
".read": "auth.uid !== null",
"$postID": {
".write": "auth.uid !== null",
"$commentID": {
".write": false
}
}
}
}
}
Why does Firebase think that any user can read/write to any location in my database?
EDIT: I haven't gotten the email in a while so I think my rules are secure.

For example this rule for users node, is not secure:
"users": {
".write":"auth.uid !== null && !data.hasChild(auth.uid)",
"$userID": {
".read": "auth.uid === $userID",
".write": "auth.uid === $userID"
}
}
Because, this rule allows any authenticated user and non-existing user to write to your users node (not secure):
".write":"auth.uid !== null && !data.hasChild(auth.uid)",
and it overwrites this rule (as if this is meaningless now):
".write": "auth.uid === $userID"
To make it secure, users rule must look like this:
"users": {
"$userID": {
".write":"auth.uid !== null && !data.hasChild(auth.uid) && auth.uid === $userID",
".read": "auth.uid === $userID"
}
}
So fix your rules, and be careful of RULES CASCADING.

Related

Firebase Rules: We've detected the following issue(s) with your security rules

appreciate this looks like this is been answered various times for individual requirements. Completely new to Firebase and I want to get some insight into this. I have been presented with the message from Firebase.
We've detected the following issue(s) with your security rules:
any logged-in user can read your entire database
any logged-in user can write to your entire database
My current rules look like this:
{
"rules": {
".read": "auth != null",
".write": "auth != null",
"items": {
".indexOn": "ownerId"
},
"events": {
".indexOn": "ownerId"
},
"contacts": {
".indexOn": "ownerId"
}
}
}
Based on the documentation, Do I simply need to do this?
{
"rules": {
".read": "auth != null && auth.uid == $uid"
".write": "$user_id === auth.uid",
"items": {
".indexOn": "ownerId"
},
"events": {
".indexOn": "ownerId"
},
"contacts": {
".indexOn": "ownerId"
}
}
}
Will users still be able to access their own (previously) written data prior to making the change while enforcing the security rules from Firebase.
Apologies if this a silly question, but got a lot of data which I cannot let users not have access to.
Thanks
As firebase documentation says:
Sometimes, Rules check that a user is logged in, but don't further restrict access based on that authentication. If one of your rules includes auth != null, confirm that you want any logged-in user to have access to the data.
So you have to get rid of this part down under the rules part:
".read": "auth != null",
".write": "auth != null",
And use any of these approaches: Content owner only, Path-delineated access or Mixed public and private access.
For example:
{
"rules": {
"products": {
"$uid": {
".read": "auth != null && auth.uid == $uid",
".write": "auth != null && auth.uid == $uid",
".indexOn": ["creatorId", "isActive"]
}
},
"stores": {
"$uid": {
".read": "auth != null && auth.uid == $uid",
".write": "auth != null && auth.uid == $uid",
".indexOn": ["creatorId", "isActive"]
}
},
"orders": {
"$uid": {
".read": "auth != null && auth.uid == $uid",
".write": "auth != null && auth.uid == $uid",
}
},
}
}

Can you control individual properties in firebase realtime database security rules?

I need to control access to individual properties under a user object in the users directory in the firebase rtdb. My current rules are as follows:
{
"rules": {
"users": {
"$uid": {
"name": {
".write": "auth.uid === 'adminID' || $uid === auth.uid",
".read": "auth.uid === 'adminID' || $uid === auth.uid"
},
"plans": {
".write": "auth.uid === 'adminID' || $uid === auth.uid",
".read": "auth.uid === 'adminID' || $uid === auth.uid"
},
"survey": {
".write": "auth.uid === 'adminID' || $uid === auth.uid",
".read": "auth.uid === 'adminID' || $uid === auth.uid"
},
"survey_done": {
".write": "auth.uid === 'adminID' || $uid === auth.uid",
".read": "auth.uid === 'adminID' || $uid === auth.uid"
},
"notes": {
".write": "auth.uid === 'adminID'",
".read": "auth.uid === 'adminID'"
},
"status": {
".write": "auth.uid === 'adminID'",
".read": "auth.uid === 'adminID'"
}
}
},
"sending": {
".write": "auth.uid === 'adminID'",
".read": "auth.uid === 'adminID'"
}
}
}
The admin should be able to access all the data, but when I login as him on my web app, I get a permission_denied error.
Also, the user (not admin) should be able to see their name, plans, survey and survey_done properties but none of the others that are admin protected. When a user makes a read request to users/their-user-id I get the same error as above. Ideally it would just return all the properties the user does have access to, but the only way the user can access these are to read request to them individually.
The query I am using is:
firebase.database().ref("users").child(id).once("value")
When you query for the contents of a specific node in the database, security rules with either allow access to the entire contents of that node and all of its children, or reject all access. You can't use security rules to filter some of the children from view.
Typically what people do is split the node into two children, and protect each child separated, based on the roles required of the user. So, in your case, there would be one node that contains name, plans, survey, and survey_done. Then there would be another node for notes and status. Each container node would have different permissions.

Firebase Database Rules to match row

I am using a Firebase Realtime Database. I have the following data:
I also have the rules:
{
"rules": {
".read": "auth != null",
".write": "auth != null",
"chat": {
"$key": {
".read": "data.child('memberId1').val() === auth.uid && data.child('memberId2').val() === auth.uid",
".write": "data.child('memberId1').val() === auth.uid || data.child('memberId2').val() === auth.uid"
}
},
The initial rule works perfectly:
".read": "auth != null",
".write": "auth != null",
Problem
The following 2 rules have no effect.
"chat": {
"$key": {
".read": "data.child('memberId1').val() === auth.uid && data.child('memberId2').val() === auth.uid",
".write": "data.child('memberId1').val() === auth.uid || data.child('memberId2').val() === auth.uid"
}
},
As you can see, in order to test these rules, in the first rule, I have made an impossible condition of memberId1 and memberId2 both equal to the users uid. As a result I would expect it to fail.
If I remove:
".read": "auth != null",
".write": "auth != null",
and just have:
"chat": {
"$key": {
".read": "data.child('memberId1').val() === auth.uid || data.child('memberId2').val() === auth.uid",
".write": "data.child('memberId1').val() === auth.uid || data.child('memberId2').val() === auth.uid"
}
},
Then access is denied. Even if I change it to:
"data.child('memberId1').val() === 'h6qQg5YfQveTaCyBEXwDMSJPqwk1'
The following is also denied:
"chat": {
"Ko7w9XTtuRVN4p6CMp7": {
".read": true,
Question
How should I structure the rules to allow that a user may only access a row where their uid matches either memberId1 or memberId2?
Thanks
UPDATE
I have the following code:
findChats(): Observable<any[]> {
return this.af.database.list('/chat/', {
query: {
orderByChild: 'negativtimestamp'
}
}).map(items => {
const filtered = items.filter(
item => (item.memberId1 === this.me.uid || item.memberId2 === this.me.uid)
);
return filtered;
});
}
My question is similar to this one. I try the following with no success:
{
"rules": {
"chat": {
"$id": {
".read": true
}
},
Firebase rules are atomic. So if you try to read /chat (and thats what you are currently doing) it will only check the /chat branch rules. Since you dont have any rule in /chat it goes for the default thats is not giving access. Therefore, your rules would only be evaluated in case you were trying to read /chat/chatId.
One possible solution you could go for is to store a list of chats which each user is part of. So you can keep your current chat branch but store another branch in the database with the following structure:
user_chats: {
uid1: {
chatId1: true,
chatId2: false
}
uid2: ...
}
And rules:
"user_chats": {
"$uid": {
".read": "auth.uid === $uid",
".write": "auth.uid === $uid"
}
}
Then you could keep your chat rules like you already have them but first get the data from /user_chats/uid and then for each chatId retrieved you you will need to read on chat/chatId.

How prevent retrieve a child in same parent node?

I want to read all usernames from users parent node, because I'm using search feature in my app (if provided searchActive: true child in users node). But email need to be reachable only by owner.
I have just tried like below, but I'm still getting email. I'm worried about security not only email, What I'm missing and How Can I organize all of them?
"rules": {
"users": {
".read": "auth !== null",
"$uid": {
".write": "auth !== null && auth.uid === $uid",
".read": "auth !== null && auth.uid === $uid",
"username": {
".validate": "
!root.child('usernames').child(newData.val()).exists() ||
root.child('usernames').child(newData.val()).val() == $uid"
},
"email": {
".read": "auth.uid == 'facebook:'+$uid || auth.uid == $uid"
}
}
},
"usernames": {
".write": "auth !== null",
".read": "auth !== null"
},
First the reason why you can still access email is because rules cascade meaning when you set read to true for the parent node all the children can also be read. In your case:
"users": {
//Read is being set to true here for everything in this node
".read": "auth !== null",
"$uid": {
".write": "auth !== null && auth.uid === $uid",
//This will be ignored, since read was allowed already
".read": "auth !== null && auth.uid === $uid",
"username": {
".validate": "
!root.child('usernames').child(newData.val()).exists() ||
root.child('usernames').child(newData.val()).val() == $uid"
},
"email": {
//This will be ignored, since read was allowed already
".read": "auth.uid == 'facebook:'+$uid || auth.uid == $uid"
}
}
},
"usernames": {
".write": "auth !== null",
".read": "auth !== null"
},
I suggest you take some time to read all the documentation about firebase security. It can really help you avoid situations like this and perhaps give you some idea's about implementing a good security for your case.
A possible solution is to use a seperate username node where you store all the usernames for your search feature. You can use the rules to make sure everyone can read it but only the owner of a specific username can change it.

Security rules for posting to a user's subdirectory in Firebase

The following are my Firebase security rules:
security-rules.json
{
"rules": {
"users": {
"$uid": {
".write": "auth.uid === $uid",
".read": "auth.uid === $uid"
}
}
}
}
It works fine when my path ends with the users directory. As in:
https://my-firebase.firebaseio.com/users/my-user-id.json
But when I try to post directly to a subdirectory, as follows:
https://my-firebase.firebaseio.com/users/my-user-id/settings.json
it doesn't work.
Question
What do I need to do to the security-rules.json file (or anything else) to be able to write directly to a user's subdirectory?
Edit:
Someone suggested, "just extend your rule to include settings." So I tried this:
security-rules.json
{
"rules": {
"users": {
"$uid": {
".write": "auth.uid === $uid",
".read": "auth.uid === $uid"
},
"settings": {
".write": "auth.uid === $uid",
".read": "auth.uid === $uid"
}
}
}
}
Which throws the following error:
9:30: Unknown variable '$uid'.
10:31: Unknown variable '$uid'.
This works in the simulator:
{
"rules": {
"users": {
"$uid": {
".read": "auth != null && auth.uid == $uid",
".write": "auth != null && auth.uid == $uid",
"settings": {
}
}
}
}
}
After further testing, I found the security rules contained in the OP also work in the simulator:
security-rules.json
{
"rules": {
"users": {
"$uid": {
".write": "auth.uid === $uid",
".read": "auth.uid === $uid"
}
}
}
}
There is no need to add additional rules for writing deeper into the node tree. The highest level permissions are sufficient.
Aside: My problem appears to be something other than the security rules I'm using. I must do more research, experimentation and testing.

Resources