Firebase security rules and authentication - firebase-realtime-database

Here's the use case:
Anyone can register to use the app. Authentication is handled by Firebase Auth.
Registered users can only read and write their own details. Access to the database is managed by Firebase security rules. For example:
{
"rules": {
".read" : "root.child('Admins').hasChild(auth.uid)",
".write" : "root.child('Admins').hasChild(auth.uid)",
"Companies": {
"$company" : {
".read" : "data.child('firebaseuid').val() === auth.uid",
".write" : "data.child('firebaseuid').val() === auth.uid",
"firebaseuid" : {
".write" : ???? ,
".read" : ????,
},
"details" : {
".read" : "data.parent().child('firebaseuid').val() === auth.uid",
".write": "data.parent().child('firebaseuid').val() === auth.uid",
},
},
},
}
QUESTION: What are the appropriate security rules for "firebaseui"?
Obviously, setting the read/write attributes true won't do. That's an open database.
"firebaseuid" : {
".write" : true ,
".read" : true,
},
Equally, :"auth !== null" doesn't work, because it would allow ANY registered user to read/write everybody's information.
"firebaseuid" : {
".write" :"auth !== null" ,
".read" :"auth !== null",
},
Setting the rule to allow only the registered user to read/write works...almost.
"firebaseuid" : {
".read" : "data.parent().child('firebaseuid').val() === auth.uid",
".write": "data.parent().child('firebaseuid').val() === auth.uid",
}
Almost, because this suffers from the First Write Problem. That is, the user would not be able to initialize the firebase uid ("firebaseuid") at registration, because in that first instance, the firebaseuid field is null, causing the security rule to return false.
One could add a firebase validator to the security rule, so that the user would be to write to "firebaseuid" when and only when the field is blank. This works well enough, if there is only one registered user. But suppose there are multiple admins per organization? In this case, this solution fails.
One might want to write a Firebase Cloud function that is triggered upon registration, but the Firebase user class doesn't contain enough information. At registration the only info available is email an password.
One could write a more complicated Cloud function triggered by a database create or http put, but every approach i can think of, has a security hole.
I realize this must be a very common problem, so there must be a simple answer. Can somebody please point me in the right direction?

You'll typically want to allow the user to set firebaseuid to their own UID, and only if it currently doesn't have a value. So:
"firebaseuid" : {
".write" : "!data.exists() && newData.val() === auth.uid"
},

Related

Correctly setup firebase database rules

I have the following rules for my database. My database structure is as follows in the database
My rules for my database are as follows:
{
"rules": {
"Users": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
}
}
}
}
If I do a query to read the books under a user or add a new book under a user, will this rule still apply and only allow users who have a correct user id to add the book? Or will I need to drill down to Books and add that rule? Something like...
Permission cascades downwards in the database. So once a user has read or write permission on /Users/$uid they also have that same permission on the Books node under there.
For more on this, see the documentation on read and write permissions cascase.

Firebase Write Rule for Item with nested approved users

I have a realtime database with users and teams. Each team has a list of approved users under settings.. I want to create a rule so only approved users can write to their team...
something like:
I would not recommend Custom Claims because they are very limited in space.
You can write the rule very easy using the exists clause:
"teams": {
"$team_id" : {
".read": "true",
".write": "auth != null && root.child('teams/'+$team_id+'/settings/approvedUsers/'+auth.uid).exists()"
}
}
This rule will also deny a creation if there is no user on that path. You can then create the team from the backend or mark some users as admins or if anyone can create a team you can add a clause that checks if there is any data before and if not the rule befor should not get used. Something like this:
"teams": {
"$team_id" : {
".read": "true",
".write": "!data.exists() || (auth != null && root.child('teams/'+$team_id+'/settings/approvedUsers/'+auth.uid).exists())"
}
}
Here we check if the team already exists and if not anyone can create it. If it exist only users under approvedUsers can change it. That way the first user creating the team can create it. But don't forget that he needs to add himself to approvedUsers.
If you are working with emails for users that are not already in the system you could use the email instead of an uid as key and just set the value of the key to true. Just make sure te remove all chars that are not allowed as key from the email withe something like:
email.replace('.','')
And also do the same in the rules like:
"teams": {
"$team_id" : {
".read": "true",
".write": "auth != null && root.child('teams/'+$team_id+'/settings/approvedUsers/'+auth.token.email.replace('.','')).exists()"
}
}
The databse would then look like:
teams
teamID1
approvedUsers
email1#test.com: true
email2#test.com: true
teamID2
approvedUsers
email1#test.com: true
email3#test.com: true

Firebase rule to avoid parent overwrite

In my db, I have a node like this on root:
-dbroot
--usernameuid
---user1:'someid'
---user2:'someid'
---user3:'someid'
...
for creating this I am using code below:
database().ref('usernameuid/' + that.state.username).set(auth().currentUser.uid).then(() => {
...
Recently, I am not sure how it happened but somebody managed to delete or overwrite all --usernameuid node.
As my investigation, I found out blank username passed despite i am checking username with regex.
Anyway, firebase got blank username (that.state.username) and overwrote like --usernameuid: 'someid' and all data gone.
For prevent this not to happen again in future, (app is live so cant modify code) what kind of firebase rule can i write for:
user can only add child to --usernameuid
can't overwrite all like --usernameuid: 'something'
can't delete anything under --usernameuid
I found solution like this:
{
"rules": {
".read": "auth.uid !== null",
...
"otherchild1": {
".write": "auth.uid !== null",
},
"otherchild2": {
".write": "auth.uid !== null",
},
...
"usernameuid": {
"$username" : {
".write": "auth.uid !== null",
}
},
Summary: No rule at parent, specify .write in child you want. But on root, you need to delete global .write rule and specify seperately for each child hence firebase does not support cascade rules.

Firebase Rules settings

I decided to use Firebase in an old objectiv C iOS app.
I installed what I needed following the instructions given here:
Add Firebase to your iOS app right after login into Firebase.
Then I followed the instructions given here:
https://firebase.google.com/docs/database/
in order to work with a database.
At this point it basically works, I can write some data.
Nevertheless I have this issue about the Rules settings.
I followed the information given here:
https://firebase.google.com/docs/database/security/quickstart
But if I set my rules like this:
{
"rules": {
".read": true,
".write": true
}
}
it works as expected. On the other hand, if I set them like this:
{
"rules": {
".read": "auth != null",
".write": "auth != null"
}
}
which is the default and should allow me to write data from my app, it does not work.
What is the problem?
If you don't want the user to login, you could automatically authenticate the user anonymously.
Authenticate with Firebase Anonymously on iOS
Then you could use:
{
"rules": {
".read": "auth != null",
".write": "auth != null"
}
}
After reading your comment in reply to cartant,it is clear that you want to allow your app users to access your database for reading and writing , without need of signing in.
I would also like to do this without requesting the user to login. Is this possible?
YES
It is possible to allow your users to access your DB without any signing process.
Actually
{
"rules": {
".read": true,
".write": true
}
}
this rule says that you are providing read and write permission to your user without signing in.
On the other hand
{
"rules": {
".read": "auth != null",
".write": "auth != null"
}
}
in this rule you are mentioning that your authentication ("auth != null")has to be performed before granting the read , write permissions to your user.
So to accomplish your goal just use the default rule i.e rule without authentication.

Firebase security and rules

Just a quickie, I'm trying to get my head around Firebase security protocols and I have set up a database called UsersDB which will store details based on auth.uid. The details being full name, email, provider, account created date, last login date.
I have setup a rule as follows:
{
"rules": {
".read": "auth != null", // only authed users can read/write
".write": "auth != null",
"UsersDB": {
"$uid": {
".read": "auth.uid == $uid", // users can read/write their own data
".write": "auth.uid == $uid"
}
}
}
My understanding is that the record will only be able to be read and written by the person whose user_id matches the auth.uid.
My question is have I done this correctly and if not how should I have achieve this? I only want the person creating the account to be able to read and write to this and no other uid to access the information.
Lastly, as a administrator of the firebase account. I would be thinking of going down the line of creating a admin console type software which would allow me access to all the data stored. How would I change or update the rules to allow an admin login to access the data above. Would I change the read access to anyone (although this would seem to me to leave a vulnerability in the rules) or is there a way to declare a rule giving my (admin) full read access to all data?
Thanks
You're overlooking a very important part of the Firebase documentation that specifies that permissions cascade:
SECURITY AND FIREBASE RULES WORK FROM THE TOP-DOWN
The child rules can only grant additional privileges to what parent nodes have already declared. They cannot revoke a read or write privilege.
Since your top-level read and write rules already allow any authenticated users to read/write all accounts, you cannot revoke that privilege lower in the tree.
Luckily there is no need in your scenario to grant these higher-level permissions.
{
"rules": {
"UsersDB": {
"$uid": {
".read": "auth.uid == $uid",
".write": "auth.uid == $uid"
}
}
}
}
With this each user can only read and write their own data.
Keep in mind that Firebase rules are not filters. With the structure above, no user can query on /UsersDB, since nobody has read permission there.

Resources