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.
Related
I made a simple app for underprivileged students so that they can learn during the Pandemic. I update notes for each subject daily through the firebase console (Cloud firestore). No authentication included because the students are small, and not well versed with technology.
I have only allowed read and deleted the write options in the security rules. Last night I got this email. I have added the image copy. I just want everyone to download the app and read the data(Notes) but no one to write. Is my database safe? Can anyone write, delete or manipulate the database if they got the project id?
Soon I'm planning to buy the blaze plan but now I'm a little insecure.
My security rules are as follows:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read;
}
}
}
email
As stated in that email/warning message, the rules you have are not considered secure. As your rules currently stand, a malicious user could abuse your database by calling hundreds of requests to it which may lead to unexpected billing charges.
The trigger for this warning is simple - if "allow read; or "allow write; is present on the /{document=**} path, send the user the warning as these broad rules are considered a bug and should be tightened. One of the main reasons the warning exists is if you store sensitive user data like phone numbers, email addresses, billing information under a /users/someUserId document - with the current rules this is now publicly accessible and can get you in toruble with data privacy laws and regulations like GDPR. There are a number of other similar conditions that also send similar warnings like if the system detects that the default 30 days of read/write access has expired.
If your data is expected to be publicly accessible, rather than grant read access to the entire database, grant it to the specific collections that you want to be public.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// public database of cars
match /cars/{carId} {
allow read;
}
// public database of trains
match /trains/{trainId} {
allow read;
}
// only that user can read/write their own data
match /users/{userId} {
allow read: if request.auth != null && request.auth.uid == userId;
allow write: if request.auth != null && request.auth.uid == userId;
}
}
}
I recommend having a read of the fixing insecure rules documentation for more information.
You can also make use of granular rules to limit the queries that can be performed against your database such as limiting getting a list of posts to 10 at a time.
yes it is safe, and people can only read stuff
no one can write to it unless they can access the database directly
it's completely safe, no 1 can write or delete your database, but since the database is open to read to everyone, hence some notorious people might send endless requests to your database and exaust ur daily limit if u using free plan or if u using biling, ur biling cost will sky rocket.
so best is to make read permission for authenticated users only, and on app side, u can do anonymous login, so that u don't have to enforce gmail or other login on app side, and unauthenticated users can't exaust ur daily limit, check the following link for anonymous login https://firebase.google.com/docs/auth/android/anonymous-auth
I have a cloud firestore. In this store I have a collections of all admins. All data from the store can be read by anyone, but writes should be limited to the admins. If someone tries to write to a collection, e.g. latest-news, I want to see if the user that tries to write to the collection is an admin.
The idea I want is something like this:
match /latest-news/{news-id} {
allow read: if true;
allow write: if <my-admin-collection>.map(auth => auth.uid).includes(request.auth.uid);
}
How would I actually do this?
If your collection of admins stores those admins by their UID, you can check for the existence of such a document from your rules with:
if exists(/databases/$(database)/documents/admins/$(request.auth.uid));
The above allows the operation if a document with the current user's UID exists in the /admins collection.
Also see the documentation on attribute-based and role-based access control.
It all depends on how you define roles.
In firebase auth roles can be configured by Custom Claims.
Custom claims can be used in writing security rules like
match /latest-news/{news-id} {
allow write: if request.auth.token.role == 'Admin';
}
Above security rule will only allow user from having role as 'Admin' to write to the collection.
I am using FireBase / Firestore for my website - I have products displayed on the website which are stored in the database. - Currently, I am having my rules set up as
match /{document=**} {
allow read: if true
allow write,create,update, delete: if request.auth.uid !=null
}
I am getting now mails from Firebase warning me about my insecure rules:
[Firebase] Your Cloud Firestore database has insecure rules
We've detected the following issue(s) with your security rules:
any user can read your entire database
Because your project does not have strong security rules, anyone can access your entire database. Attackers can read all of your data, and they can drive up your bill.
How do I secure my database properly but allow my website to read product data withour requiring the visitor to sign in?
The email is warning you that anyone can read any document in your entire database because of match /{document=**}. You should avoid using this global wildcard entirely, since it can lead to unexpected security issues. You should instead call out each individual collection with specific access for that collection. Minimally, it will look more like this:
match /collection1/{document=**} {
allow read: if true
allow write,create,update, delete: if request.auth.uid !=null
}
match /collection2/{document=**} {
allow read: if true
allow write,create,update, delete: if request.auth.uid !=null
}
Whether or not this form is "proper" for your app is not clear. Your rules need to encode the specific permissions for your app. Every app is going to be different, and your rules need to be tailored to your security requirements. You are effectively writing application logic into the rules, so treat it just like any other code.
i have a serious Question. I am developing the Security Rules for my Firestore Database. So what if someone decompiled my App, stole the GoogleInfo.plist, added this file to his Project, and creates multiple Accounts with it? I mean in the security rules you have to:
allow create: if request.auth != null;
So he could add a new Document every time he adds an FirebaseUser Account.
How to solve and secure this?
Are there other options like sign in with custom field at example:
I create a document ID.
and so we check in the Firestore rules:
match /document/{myDOC}
allow write: if request.auth.code == myDOC;
So what I mean here is, if I can set additional Information to the Request of my App, and check if the additional Information is Equal to the myDOC;
Thanks!!
This is all working by design. There is no "security" information in GoogleInfo.plist. It just contains data that instructs the Firebase SDK on how to find your project and its resources. Without that data, your app would know nothing about your project.
To secure your database, you will need to design your database to allow for per-user security, then write rules that determine which authenticated users can read and write which documents, as suggested in the documentation.
It's not possible to send extra information along with a query for the purpose of security. You should depend on what Firebase auth provides in request.auth in the rules language.
See also: Is it safe to expose Firebase apiKey to the public?
https://firebase.google.com/docs/auth/admin/custom-claims
Firebase Admin SDK allows you to define custom attributes on user accounts.
admin.auth().setCustomUserClaims(uid, {admin: true}).then(() => {
// The new custom claims will propagate to the user's ID token the
// next time a new one is issued.
});
When you are writing your rules, it is possible to check these custom attributes.
{
"rules": {
"adminContent": {
".read": "auth.token.admin === true",
".write": "auth.token.admin === true",
}
}
}
But as it is stated in the docs, you should consider these points:
Use custom claims to store data for controlling user access only. All other data should be stored separately via the real-time database or other server side storage.
Custom claims are limited in size. Passing a custom claims payload greater than 1000 bytes will throw an error.
I'm working on Admin module of an android application. Admin module is a web-app based on Angular. Firebase auth(Email/password) is used to sign-In as a admin. I've added a manual credential entry to firebase and admin is using these credentials to login (Since there is no registration functionality for admin)
on other side Android developer has also used the same Auth method to sign in a user. So users of android application are able to login with their credentials to Admin module.
How do I prevent android users from login to web-app. Is there any method or rule that I can use to filter the incoming login request and allow login only if email belongs to Admin ?
Firebase has no knowledge of what an "Admin" is here. That's a concept that is specific to your app, so you will have to enforce it.
There's no way to allow certain users to only sign in on a specific platform. This is because Firebase makes a clear split between authentication (the user proves who they are) and authorization (the user has access to a resource). You use Firebase Authentication for authenticating the users, but will the "who can use what app" is an authorization problem, so it is handled elsewhere.
If you're using Realtime Database, Cloud Firestore, or Cloud Storage through Firebase, you'll for example typically enforce our authorization logic in Firebase's server-side security rules. Since these are automatically enforced on the server, there's no way for a user to bypass them, and they apply equally no matter what platform the user is on.
For example, a common first security rule that I start my Firestore projects with is:
service cloud.firestore {
match /databases/{database}/documents {
match /chat/{document} {
allow read;
allow write:
if isAdmin()
}
function isAdmin() {
return false;
}
}
}
This allows anyone to read the data, and no-one to write it, since isAdmin always returns false. With these rules the only way I can write data is by using an Admin SDK, since code using this SDK runs with elevated privileges and bypasses the security rules. A perfect way to get started, and safely populate my database with initial data from Node.js scripts (in my most common case).
Then at some point I do as you did, and add an application administrator. At that point I add their UID to the security rules:
function isAdmin() {
return request.auth.uid == "KqEizsqUQ6XMkUgLUpNxFnzLm4F3"
|| request.auth.uid == "zYXmog8ySOVRrSGVt9FHFr4wJb92";
}
So the above function in my rules now gives two specific Firebase Authentication users write access to the data.
This approach works well for the first few users, but at some point adding UIDs to the rules gets tedious and error prone. At that point I have two main options:
Store the UIDs of the application administrators in the database.
Identify application administrators in another way.
For storing the UIDs in the database you'd typically either add those UIDs to the database manually, or allow administrators to identify other administrators, and write their UIDs from the app. Either way, the security rules for this are something like:
function isAdmin() {
return request.auth.uid == "KqEizsqUQ6XMkUgLUpNxFnzLm4F3"
|| request.auth.uid == "zYXmog8ySOVRrSGVt9FHFr4wJb92"
|| exists(/databases/$(database)/documents/admins/$(request.auth.uid))
;
}
So the last line now also recognizes any authentication user whose UID is store in the admins collection as an application administrator.
Finally, say that I want everyone from my company to be an application administrator; I'd do that with:
function isAdmin() {
return request.auth.uid == "KqEizsqUQ6XMkUgLUpNxFnzLm4F3"
|| request.auth.uid == "zYXmog8ySOVRrSGVt9FHFr4wJb92"
|| (request.auth.token.email_verified && request.auth.token.email.matches(".*#google.com"))
|| exists(/databases/$(database)/documents/admins/$(request.auth.uid))
;
}
So this means that any Firebase Authentication user who has a verified #google.com email address is now an also application administrator.
As you can see, I build these rules up in multiple steps, starting with simply identifying that I will have application administrators who have specific permissions and creating the isAdmin function.