Firebase DB rules simulator allow read and write by unauthenticated users - firebase

The simulator allows read/write to Posts key, but the results are correct for the Users key rules. Each post under Posts has a uid value representing a user in Users key.
Are my rules wrong or is the simulator wrong? Be gentle, I'm new to Firebase. :)
Two equals:
Redacted Data view: https: // i.stack.imgur.com/GaYMj.png (remove spaces around "//")

Try changing your rules to check that a uid child exists. For example:
".read": "data.child('uid').exists() && data.child('uid').val() === auth.uid"
Based on a quick test, I think what is occuring is that when a uid child does not exist, the evaluation of data.child('uid').val() fails and is handled by assigning it a value of false. Similarly, because the user is not authenticated, auth is null and auth.uid also evaluates to false. So your rule effectively becomes ".read": "false === false", which is true.
When I first simulated a read using your rule and I did not have a uid child in my database under /posts/1, the read was granted, as you reported. When I added a uid child, it was not granted.

Related

Firebase database security rule, check auth.email in resource.data

Error [firebase.firestore] FirebaseError: Missing or insufficient permissions.
I have an object that a few users are allowed to update. An admin will set their email, and these users who sign in with those email will be allowed to update.
These are the rules that I have tried:
allow update: if request.resource.data.managerEmails.val().contains(request.auth.email) && request.resource.data.id == resource.data.id;
allow update: if request.resource.data.managerEmails.contains(request.auth.email) && request.resource.data.id == resource.data.id;
allow update: if request.resource.data.managerEmails.includes(request.auth.email) && request.resource.data.id == resource.data.id;
The resource to update:
{
id: "someid",
...fields,
managerEmails: "abcde#email.com,anothermanager#email.com",
}
User auth who is updating:
{
uid: "rSTLnYD9aisyZJHQPC6sg7mlsZh1",
email: "abcde#email.com",
...
}
Update:
Using request.auth.uid has been working in other rules, but in this case, I have to use emails because the users might not have signed up yet.
Using Rules Playground, I get Property email is undefined on object. Maybe using request.auth.email is not possible?
I was having the same problem but switched from using firebase.auth.email to firebase.auth.token.email and it now works.
I strongly suggest doing two things.
Firstly, store UIDs instead of email addresses. A UID is the preferred way to identify a single Firebase Auth account and is always guaranteed to exist and be unique. Email addresses are not.
Secondly, store the list of users as an array type field instead of comma separated string. This will be much easier to manage overall.
After you do these two things, the rule becomes simple:
allow update: if resource.data.managerUids.hasAny([request.auth.uid]);
use: request.auth.token.email insted request.auth.email
documentation here

How to hide specific node in firebase realtime database

I have 2 questions related to firebase realtime database.
Q1) Is it possible to hide a certain node from public and only one person have access to that node ?
( I want to save a transaction key in database and only 1-2 persons can access that key from database. )
Q2) If my security rules for database are set to public, can anyone download/access complete data or they must know the structure in order to access data from database.
A1/ Yes it is possible to limit access to a certain node to a only subset of users. There are several approaches for that. If it is a small and more or less stable subset of users, you can check if their uid is contained in a specific node listing the admin users uids, as shown below:
{
"rules": {
"secretnode": {
//only an admin user can read
".read": "auth != null && root.child('adminusers/' + auth.uid).exists()"
},
"adminusers": {
".read": false,
".write": false
},
"othernodes": {
".read": true,
".write": true
}
}
}
And, for example, in the database you save the admin uids as follows:
DBRoot
- adminusers
- uid1
- name: "aaaaaa"
- otherDataItem: "xxxxxx"
- uid2
- name: "bbbbb"
- otherDataItem: "yyyy"
A2/ Yes, if your security rules for database root are set to public, "anyone (can) download/access complete data". Note that, as explained in the documentation:
Shallower security rules override rules at deeper paths. Child rules
can only grant additional privileges to what parent nodes have already
declared. They cannot revoke a read or write privilege.
In other words, this means that if your security rules for the database root are set to public anyone can download the full JSON tree representing your data, without the need to know its structure.

Firestore Security Rules: If timestamp (FieldValue.serverTimestamp) equals now

How do I check if user on client sided created document with only firebase.firestore.FieldValue.serverTimestamp()?
I have following:
allow create: if request.resource.data.timestamp == ??
What should I have instead of ??. I have tried serverTimestamp() firebase.firestore.FieldValue.serverTimestamp(), now or now() but it doesn't work.
It is possible to do it in Firebase like this:
".validate": "newData.child('timestamp').val() === now"
I am looking for the same solution. Any ideas? Thanks
You can access the current request timestamp in Security Rules using the request.time attribute (docs), which is the Firestore equivalent to the Realtime Databases's now. You'll therefore want something like:
allow create: if request.resource.data.timestamp == request.time;
For serverTimestamp() this should evaluate to true.
You should always validate client input in Security Rules, even if you're using serverTimestamp(). Security Rules doesn't automatically know the server input the value instead of the client, so without this check, a malicious client could create a different created at time.

Firebase database rules only two out of three conditions work

I have "UID" database (uid root for each user).
I try to share "uid" roots between users, and i want to give read access to "UID" root in one of three cases:
1. The user who access is the owner.
2. The user who access is located in "UID\PERMITTED_USERS" of target root.
3. The user who access is located in "UID\TEMP_USERS" of target root.
To accomplish this I created next rule:
".read" : "$uid === auth.uid || (root.child(root.child(auth.uid).child('PRE_SHARE').val()).child('TEMP_USERS').hasChild(root.child(auth.uid).child('TEMP_PERMIT').val()) || root.child(root.child(auth.uid).child('CURRENT_SHARE').val()).child('PERMITTED_USERS').hasChild(auth.uid))"
But I was disapointed to discover that only the first two conditions are checked, and the third is not. (I changed the order of the conditions and every time I could access using the first two in a row).
Is there a way to solve this?
EDIT:
Adding db example:
So after lots of tests I found the problem. My code deletes PRE_SHARE as well as TEMP_USERS, and when rule tries to access val() of non existent PRE_SHARE it gets null pointer exception. Too bad Firebase doesn't write this exception, it would save me lots of time...
".read" : "$uid === auth.uid ||
(root.child(auth.uid).hasChild('PRE_SHARE') &&
root.child(root.child(auth.uid).child('PRE_SHARE').val()).hasChild('TEMP_USERS') &&
root.child(auth.uid).hasChild('TEMP_PERMIT') &&
root.child(root.child(auth.uid).child('PRE_SHARE').val()).child('TEMP_USERS').hasChild(root.child(auth.uid).child('TEMP_PERMIT').val())) ||
(root.child(auth.uid).hasChild('CURRENT_SHARE') &&
root.child(root.child(auth.uid).child('CURRENT_SHARE').val()).hasChild('PERMITTED_USERS') &&
root.child(root.child(auth.uid).child('CURRENT_SHARE').val()).child('PERMITTED_USERS').hasChild(auth.uid))"

Firebase database rules – `data.exists()` always seems to be true, possible bug?

I am trying to secure my firebase database to allow the creation of new records, but not allow the deletion of existing records. Ultimately, I plan to utilise Firebase authentication in my app as well, and allow users to update existing records if they are the author, but I am trying to get the simple case working first.
However! No matter what I try in the database rules simulator, despite what the documentation seems to suggest, the value of data.exists() seems to always be true. From what I what I can understand from the documentation, the variable data represents a record in the database as it did before an operation took-place. That is to say, for creates, data would not exist, and for updates/deletes, data would refer to a real record that exists in the database. This does not seem to be the case, to the point where I am actually suspecting a bug in Firebase, as when setting the following rules on my database, all write operations are disallowed:
{
"rules": {
".read": true,
".write": "!data.exists()"
}
}
No matter what values I put into the simulator, be it Location or Data. I have even written a small EmberJS app to verify if the Simulator is telling the truth and it too, is denied permission for all write operations.
I really have no idea where to go from here as I am pretty much out of things to try. I tried deleting all records from my database, which lets the simulator think it can perform write operations, but my test app still gets PERMISSION_DENIED, so I don't know what's causing inconsistencies there.
Is my understanding of the predefined data variable correct? If so, why can't I write the rules I want? I have seen snippets literally trying to achieve my "create only, no-delete" rule that seem to line up with my understanding.
Last note: I am trying this in a totally new Firebase project with JUST the rules above, and only ~a few records of junk data laying around my database.
Because you have placed the !data.exists() at the root location of your database, data refers to the entire database. You will only be able to write to the database when it is completely empty.
You indicate that you run your tests with only a few records of junk data laying around my database. Those records will cause data.exists() to be true.
You can achieve your goal by placing the !data.exists() rule in your tree at the specific location where you want to require that no data already exists. This is typically done at a location with a wildcard key, as in the example you linked:
{
"rules": {
// default rules are false if not specified
"posts": {
".read": true, // everyone can read all posts
"$postId": {
// a new post can be created if it does not exist
// existing posts can only be edited by their original "author"
".write": "!data.exists() && newData.exists() || data.child('author').val() == auth.uid",
".validate": "newData.hasChildren(['title', 'author', 'timestamp'])",
}
}
}
}

Resources