I have a web app that is basically a list of public items, non authenticated users can search the database and list the search results from the collection. Until I started getting daily emails from Firebase about "insecure rules in my Firestore database", I thought everything was OK, but now I am doubting them.
Here are my rules:
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read: if true;
allow write: if request.auth.uid !=null;
}
}
}
I am new to Firebase/Firestore and trying to understand:
1- The data is meant to be public but getting emails from Firebase, are these rules actually an issue?
2- Although the data is meant to be public, it would suck if someone would just drop by and download entire collection in one go. Thus, is possible to restrict public access a little by somehow preventing access to the collections/documents unless the request comes from the web app/domain? Maybe some form of token? Or some other approach?
Right now, your rules allows public read access to all documents in your database, as well as write access to all documents to authenticated users. Firebase generally considers that insecure.
The global, recursive /{document=**} wildcard is kind of dangerous since it might apply to data that you didn't intend to be readable or writably. You should instead call out the specific names of the collections in individual rules. That way, if you create new collections, they will not be automatically included with the wildcard.
In general, your rules should be as specific as possible and not depend on a global recursive wildcard.
Related
I'm trying to design a firestore schema that allows many groups of people to edit only their own group's documents, but anyone service-wide can read them. It doesn't appear possible with firestore's security rules setup.
Role-based access
Firebase supports role-based access control, but it applies service-wide to documents, and I wouldn't be able to support many groups.
Custom roles are created and assigned via the gcloud console, so I could not create new, dynamic custom roles for each group, in order for them to have their own groups.
Firestore triggers to copy documents
I considered using firestore triggers (onCreate, onUpdate, onDelete) to copy documents to other user's subcollections who are in the same group. The issue with doing that is it could create an endless loop of triggers since each member can update a document. You could in theory use a property set on a copied doc that could prevent that, but it feels a little kludgy and brittle.
Is there a best practice, or is this not possible with Firestore?
Firebase Authentication with Custom Claims might be a good fit for this use case. You can add a claim groupId and set it's value to the ID of group that user belongs to. If user can be a part of multiple groups then store an array of groupIds. Your can check if this groupId is included in user's claims in security rules.
You would have to store the groupId in every document so we know which group that doc belongs to. You can try the following rules:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /collection/{docId} {
allow read, write: if resource.data.groupId == request.auth.token.groupId;
}
}
}
The above rules will allow users to read, write a document only if groupId in document matched groupId in their custom claim.
The custom claims can be changed using Firebase Admin SDK in a secure environment only so you might have to use a Cloud function/server to add users to their respective group.
How do I lock down access to top-level collections? I want to prevent people listing or deleting top-level collections.
Would this ruleset prevent listing and deleting top-level collections, but allow complete access to documents?
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
allow get;
allow list: if false;
allow create;
allow update;
allow delete: if false;
match /{document=**} {
allow get;
allow list;
allow create;
allow update;
allow delete;
}
}
}
It seems like it might not actually do anything to collections.
Does anyone know how to affect top-level collections in Firestore rules?
Since there are no web, mobile, or REST APIs to list, delete, or otherwise operation on collections individually, you don't have to worry about that happening. Security rules only apply to the operations you can perform with web, mobile, or REST clients (when authenticated with a Firebase user).
On top of that, collections are not even really entities in Firestore. They are more like units of organization that provide indexing for contained documents. All operations effectively target documents within collections. That's why security rules makes you choose documents with match blocks.
Another thing to note - when writing security rules, you should be thinking about them in terms of specific queries that you want to allow. If you're imagining a situation that doesn't have specific client code to match, then you're not thinking in terms of what security rules are intended do.
I'm working on a PoC (Proof of Concept) application that users can download wallpapers to their device. All of the data is served using Cloud Firestore & Storage.
There is NO LOGIN for this application. Anybody can download the application, and immediately download the wallpapers she or he desires.
Bearing that in mind... I would like to have a counter that tracks how many times each specific wallpaper was downloaded.
While I have it "working" - I am questioning the rules I have set up in Firebase..
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read;
allow write: if request.auth != null;
allow update: if request.resource.data.counter is number;
}
}
}
My thought process with the rules above:
Anybody can read.
Only authenticated users can write. (I am managing all of the data with a headless CMS; Flamelink; so the 1 and only authenticated user is myself.)
Update the counter if the data is a number..
It's that last rule that I am questioning.
Is this a safe method of security to deploy to production?
Again - no login for this application, users can download all of the wallpapers with no authentication, and there will be a counter next to each wallpaper for users to see how many times each wallpaper has been downloaded.
The rules are not "secure" by any normal definition of that word. Here's what they allow. Anyone with an internet connection who knows the name of the project (which is easy to get) can:
Query for any document in the database
Update any existing document with any numeric value for counter in any document (it doesn't even have to increment, or be a positive integer)
On top of that, anyone who is able to get their Firebase Auth ID token (again, not terribly difficult for determined attacker), can fully create and write any document in the database. If you say there is no Auth at all in your app, then this is not really an issue, but if your project is configured to allow any form of authentication, it would be possible for an attacker to start writing anything.
You will get email from Firebase saying that your rules are not secure, mostly because you're allowing everyone to read everything.
What you should do is define more specifically what your security requirements are, then translate those into rules that actually meet those requirements. If you want to allow unauthenticated write access to your database of any kind, you are in a bit of trouble, as there is no way to make sure that the access actually matches the download behavior you're trying to measure. You'll be better off counting the downloads in whatever process manages the download, which is probably going to require a more sophisticated backend than what you have now.
But if allowing public read/write access is indeed what you want to allow, you'll be better off by making your rules more specific. For example, you can limit which collection the anonymous user can write to, and making sure they can only ever increment the counter, if these are the things you want.
We have an iOS app that uses Firestore. The app only reads data (no writes), we have no user accounts and nothing on the database must be protected. For that reason we ran with these naive security rules
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read;
}
}
}
However, Firebase has warned us (sure, for good reasons) that this puts us at risk for two reasons:
Any user can read anything from the database, therefor nothing is secure.
I don't see this as a problem because we have nothing that must be kept secret. At least for now, sure, that might change.
Unlimited reads are permitted
This is were the problem lies. I suppose a malicious user could read our database millions of times and thereby shut us down or simply force us to pay a large bill (we use the Blaze plan, pay as you go).
How can we protect us from this? I've seen similar questions here but they don't provide any helpful suggestions, How to limit rate of data reads from Firebase?
My idea is that we could use anonymous user accounts in the app and then in our security rules only allow read if user is "logged in" (thereby anyone using the app). I'm far from an expert on Firebase and security issues, but wouldn't this at least make it harder or impossible, for someone to read our database millions of times because it must be done through the app?
Any other suggestions how we may approach this?
There's not really a way to throttle the read rate for a particular client app once you allow read access to a document. With fully public read rules, everyone on the internet could read documents repeatedly to run up a bill. The chances of that happening are really slim though, and you should report what appears to be abusive behavior to Firebase support.
You can get rid of the warning message by calling out the individual top-level collection you would like the clients to be able to read. Since Firebase doesn't know which collections you might want to allow or disallow access to, you should be explicit. For example, something like this for each collection:
match /collection1/{document=**} {
allow read;
}
match /collection2/{document=**} {
allow read;
}
If you do this, be sure to remove the rule you have now that allows access to all documents.
If it's just read only operation then you can consider using remote config, as remote config is completely free.
But if it's necessary to use firestore database for your scenario, then consider Firebase Anonymous sign-in and setting the following security rule
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /collection_name/{documentId} {
allow read: if request.auth.uid != null;
allow write: if false;
}
}
}
I am trying to write security rules for my Firestore-based app and I am confused as to whether access to documents/collections in Firestore is the default (meaning that I need to write rules to specifically forbid access to any documents in the cases where I should limit access) or if access to documents is not the default (meaning that I need to write rules to specifically allow access in the cases where I want to enable access)?
From the firestore documentation I read this:
Every database request from a Cloud Firestore mobile/web client library is evaluated against your security rules before reading or writing any data. If the rules deny access to any of the specified document paths, the entire request fails.
From this, it seems that all documents are accessible by default but I am not certain and wanted to ask.
Any clarification would be greatly appreciated! 😅
Access is denied if not explicitly granted by any rule. That means that collections that are not matched by any explicitly declared path or wild-card pattern will not be accessible.
The first match statement is the mandatory /databases/{database}/ pattern and syntactically you must declare at least 1 allow statement within the match clause. So by default, a locked database will look like this
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if false;
}
}
}
Because omitting the if false would grant access to everything.
When you start declaring specific rules for collections, you will implicitly deny access to collections and path patterns that are not explicitly granted access.
The easiest way to confirm that is to test it with the built in rules simulator in Firebase console.
You can test this for yourself pretty easily in the console simulator. Adjust the rules so that only a certain collection is protected in some way, then try to simulate any access anywhere else at all. All reads and writes will be rejected.
You have to allow access to a document at some point in order for a mobile client to access it. Once you do that, you can not reject access to that document on any other condition for any other rule for the same access.
So the general rule is: a user can't access anything by default, but once they have any rule that allows access, it can not be rejected again by some other rule.