For each security rule, is it necessary to always check auth !== null? It seems redundant to have to do this for every child rule.
I checked Firebase's own Firechat security rules and the enforcement is not consistent at all. There are rules that use auth.uid but do not check for null auth.
What happens if auth is null and auth.uid is used in the rule?
And what is the recommended practice regarding checking auth !== null?
You need to check auth !== null if you want the data restricted to any authenticated user.
You need to check for auth.uid == $uid when you want the data restricted to the currently authenticated user. You don't need to check for auth == null && auth.uid != $uid because auth.uid == $uid will evaluate to false if the auth variable is null. But you can still include both to be thorough.
So essentially, auth != null is restricts the data to any authenticated user, and auth.uid != null restricts to the single currently authenticated user.
Now for some extra curricular information.
Use the Bolt compiler to simplify common rules.
Security Rules are flexible, but they don't have much convenience for reproducing common rules. For that, you can use the Bolt compiler.
The Bolt compiler allows you to create types and assign them to paths in your Firebase database. These types act as schema. You can also create functions that abstract common rules.
I wrote a blog post on securing user data with Bolt. It goes through what you need to know to keep user data secured with types and functions in Bolt.
isCurrentUser(uid) = auth != null && auth.uid == uid;
isAuthenticated() = auth != null
path /users/$uid {
read() = isAuthenticated() // any authenticated user can read
write() = isCurrentUser($uid);
}
In the example above we reuse the isCurrentUser() function three separate times. This will make changes moving forward much easier to deal with.
Related
I'm using Firebase anonymous auth to control access to my Firestore data without requiring the hassle of registration.
After reading many posts and looking up the documentation as best I can (I'm still learning), my understanding is
Firebase Anonymous auth is not as secure as other Firebase auth options
Anyone with the right skills could use my API key to create a UID granting access
To mitigate this, we use rules (in this case Firestore rules)
So I created rules for all my collections, making some collections 'get' only:
match /collection/doc {
allow get: if request.auth != null;
}
Other collections where the document ID must match the UID:
match /collection/{uid} {
allow get, write: if request.auth != null && request.auth.uid == uid;
}
Which is great, and seems to work well when I tested it; however, there is one collection that will contain data that is 'get' only, but I consider sensitive (names and work phone numbers).
What I'm trying to understand is:
Is it possible for anyone who gains access "with malicious intent" to obtain a list of all collections, and everything inside, thereby giving them the ability to go in and access sensitive documents that are 'get' only? As in:
allow get: if request.auth != null;
I'm using 'get' instead of 'read' because I heard that 'get' prevents a list query from being executed via the admin SDK.
My idea being that if they can't get a list of collections / documents, they won't be able to access the sensitive data because the path will be unknown to them. Or is this a naive assumption?
Firebase Anonymous auth is not as secure as other Firebase auth options
Anonymous auth is just as secure as most other sign in methods, it just doesn't give you any knowledge about who the user is. But they still get a UID assigned to them by Firebase, which allows you to secure data access for that user.
With this rule you showed:
match /collection/{uid} {
allow get, write: if request.auth != null && request.auth.uid == uid;
}
A user can only read and write the document that matches with their UID. So no matter if they signed in anonymously or with another provider, they can only read/write their own document.
So these rules indeed don't allow any user to get a list of other user documents. Even if you allowed the list operation (or the combined read operation), a read operation for the entire collection would get rejected as the request.auth.uid == uid condition requires that they only read their own document.
I want to allow specific persons to edit the data on the Firebase Database that users are not allowed to edit.
I am allowing by simply allowing them to write with their user ids:
".write": "auth != null && auth.uid === 'h7yic7LeS123asdfsdgwPrfKZ2'"
Is this a safe method? Do I have to use token based authentication to assign roles to the moderators?
If you use a standard way to authenticate your user (i.e. login) like, e.g. Firebase UI (https://github.com/firebase/firebaseui-web), this is indeed a safe method.
As a matter of fact, using such an authentication mechanism will populate auth.uid with the uid of the user and therefore only user with uid h7yic7LeS123asdfsdgwPrfKZ2 will be able to write to the corresponding database node.
Since you are mentioning "specific personS" (i.e. more than one user) you may use a different approach than hardcoding the users' uid in the security rule. You could, for example, use a rule like this one:
".write": "auth != null && root.child('admins/' + auth.uid).exists()"
In this case you would declare the uids of the authorised persons as children of the "admins" database node:
-admins
-h7yic7LeS123asdfsdgwPrfKZ2: true
If i write write: "auth != null" who can write ?
Only the person which has my google account and password or all persons who have a google account?
I want to be the only one who can write to the database page but I can't change the rule to false.
If I write: ".write": "auth != null" who can write to my database?
As Frank said, only people who have authenticated using Firebase can write with this rule. If you have no way for users to sign up for your app, then you'd be the only one who can write. If you have users create an account, they're "authenticated" as well and can write. If you want to limit writing just to your user account, get your user id and use this rule where MY_USER_ID is your uid, or look into custom claims.
".write": "'MY_USER_ID' === auth.uid"
This assumes you want to write from some interface other than directly in Firebase. If you want to prevent all writing and manually upload/edit copy directly into Firebase, you can set:
".write": false
...which would prevent all writes from users anywhere, but which would still allow you to edit your data in the console.
If you have this rule:
".write": "auth != null"
Any user who is authenticated can write to your database. That means anyone who signs in with a method that you've enabled in your Firebase Authentication console .
After reading lots of tutorials and StackOverflow's questions I still couldn't figure how to do it.
All the tutorials start with the following rule:
"users":{
"$uid":{
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
}
}
But how to allow a new user in the users node when a new user log in (auth != null) and keep the read and write user-specific path privileges ?
In other words:
Is there a way to make a new user be inserted in the users node after login, and at the same time restrain him to only read and write to his own node ?
Another question. I have a write rule in another node with newData.exists(). In the Simulator when I try to write null data to the node it denies it as expected, but it allows me to write null to a child of this same node. Shouldn't the node write rule cascade to its children ?
The rules you have above will match $uid to auth.uid. So, for example if a user's UID is cKzMyjImSBX6ybzeCCCjf0qbTym1, they will be only be able to read & write at /users/cKzMyjImSBX6ybzeCCCjf0qbTym1 in your database, regardless of whether or not this key currently exists (if it doesn't exist when written to, it will be created).
From the Firebase documentation on rules:
Once a user authenticates, the auth variable in your Firebase Database
Rules rules will be populated with the user's information. This
information includes their unique identifier (uid) as well as linked
account data, such as a Facebook id or an email address, and other
info.
In my application I have a number of different user groups, such as chat rooms and specific projects.
How can I allow the users in those groups to share their presence data, and see the presence of every other user in the group, but keep that data private from the outside world?
First, you'll need to begin authenticating your users by using one of the supported Firebase authentication mechanisms. More information about authentication in Firebase is available at https://www.firebase.com/docs/security/authentication.html.
Once you have begun authenticating users, your users' secure, verified user data will be available to you in your security rules via the auth variable. Let's assume that you have authenticated users and each user has a unique id, accessible via auth.uid.
For sharing group presence, I would recommend storing your data using a structure such as:
/groups/<group-id>/users/<user-id>/<presence-status>
Using this structure, you could write security rules that would make presence data globally private while user's could only view the presence state for each user in groups they're permitted to and only edit their own user's state. Here's an example security ruleset that enforces these restrictions:
{
"groups": {
"$groupid": {
// Users can view the presence state of users in this group if they
// are authenticated and listed in the group themselves.
".read": "auth != null && data.child('users').hasChild(auth.uid)"
"users": {
"$userid": {
// Users can update only their individual account data.
".write": "auth != null && $userid == auth.uid && newData.val() != null"
}
}
}
}
}
In the above example, only users in /groups/<group-id>/users/ have permission to see the presence data for the group, and each user can only modify their individual data. User can read / write only to their individual user node.
To extend this a bit further, let's say that you have a special class of users that are the only ones allowed to create groups. You could include the user's permission level when generating the authentication tokens (for the example below, we'll set isAdmin=true), and grant that special level of access using updated security rules. For example, if only certain users are allowed to create groups, you could update the .write rule under $groupid to look like:
"$groupid": {
// Only admins can create new groups.
".write": "auth != null && auth.isAdmin === true"
//...
}