I have a news application connected with Firebase and I add the news manually in Firestore only, inside 5 Collection, I am not good at using Firebase, I use the test mode and now I need your help in changing it, the application is for news, meaning no one can add in it only read the data from Everyone without even logging in, I get many security messages from Google, please write me the code here because I browsed a lot of questions here and I didn't understand anything and it seems that I need a course on Firebase security.
I use a pay-per-use plan, and I'm afraid of high bills. Please help me with a strong security that protects me from fake reading or even writing.
It is hard to answer without an understanding of your firestore collection names but here is a general solution.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// in this example, only admins can write or delete, but anyone can read.
match /news/{docId}{
allow read;
allow write, delete: if isAdmin(request);
}
// This is an example for a users collection, where the doc ID is the
// users authentication Id
// Note the userId=** -> this will apply rules to all subcollections
match /users/{userId=**}{
allow read: if isAuthenticated(request); // logged in users can read
// Only admins or the user with a matching doc Id can write.
allow write: if isAdmin(request) || isOwner(userId);
allow delete: if isAdmin(request); // only admins can delete
}
function isOwner(userID){
return request.auth.uid == userID;
}
function isAuthenticated(request){
return request.auth != null;
}
// this requires you to have set up custom claims
function isAdmin(request) {
return request.auth.token.admin
}
}
}
To learn more about protecting your collections with role based authentication, take a look at Custom Claims
Related
From the Firebase Security Rules docs,
Remember that any time your rules include a read, like the rules below, you're billed for a read operation in Cloud Firestore.
service cloud.firestore {
match /databases/{database}/documents {
// For attribute-based access control, check for an admin claim
allow write: if request.auth.token.admin == true;
allow read: true;
// Alterntatively, for role-based access, assign specific roles to users
match /some_collection/{document} {
allow read: if request.auth.token.reader == "true";
allow write: if request.auth.token.writer == "true";
}
}
}
But I couldn't see any Firestore get operations like get(/databases/$(database)/documents/users/$(request.auth.uid)). So how this above rule is billed against firestore?
You are right: The rules in your question, which check custom claims, don’t incur Firestore reads.
It is most probably a copy/paste error in the documentation: If you look at the doc page, the block above the one you pasted in your question has the exact same sentence (and this time this sentence correctly applies to the code example, which includes the get() function).
(FYI I’ve used the feedback button at the bottom of the doc page to report the problem to the Firebase team)
This was asked quite a lot but I still don't know what I can do to silence the warning I am getting every week from Firebase ...
Your Cloud-Firestore database has insecure rules
As you can guess my rules are all can read & write.
My problem is that I actually need that. Inside the SignUp process I need to access all the users emails and usernames to check if the currently selected is available or not. And after the User has signed up he is able to search for other users and create/delete/change his own files.
I don't quite get how I can get rid of the security issue. Where is the threat ?
As I said in then comments if your users can create/delete/update only his own files then your rules wouldn't be read/write true. As i understand you need to have all users to be able to read but you want them to write only to their own documents.
On way to do it is by putting the document id the same as the user id and then use the below rules to check if it is valid.
match /users/{userId} {
allow write: if request.auth.uid == userId;
allow read: true;
}
So, finally got it right. With this solution everyone can read and user can only write their own stuff.
Here is my code:
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
allow write: if request.auth.uid == userId;
allow read;
match /{allSubcollections=**} {
allow write: if request.auth.uid == userId;
allow read;
}
}
}
}
I have an angular app connected to a Cloud Firestore and I am still trying to work out how to specify the rules. As of now I have the use case where a user is a kitchen (this is an app for a student dorm), which means that there can be X number of users, one for each kitchen, and I have the following rules:
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
//Only allow authenticated users to read
allow read: if request.auth != null;
}
match /kitchens/{userId}/{document=**} {
// Only the kitchen that is logged in can write to its own data
allow write: if request.auth.uid == userId;
}
}
}
The thing is that since there is a certain number of kitchens, the register screen lets the user choose which kitchen that wants to register from a dropdown list. The list is then filtered based on which kitchen id's that have not already been registered, so I need permission to read the kitchen id's. I just don't want anyone but the kitchens themselves to read the data that belongs to that kitchen. I tried with:
match /kitchens/{id=**} {
// Unauthenticated users can read kitchens
allow read: if true;
}
But sending a http request with Postman just gives me all that I asks for that exists under the kitchen.
So is there a way to only be able to read the fields of the root document and not any of the collections contained in the root, so I can see which kitchens that are already registered, but not e.g. the residents of the kitchen?
This part of the rules is causing you trouble:
match /{document=**} {
//Only allow authenticated users to read
allow read: if request.auth != null;
}
You might think that it's only affecting the "root" documents, but it's actually affecting all documents. That ** will match any document at any depth. With this rule in place, any authenticated user can read every single document in the database.
You should remove this rule and replace it with one that more specifically addresses your requirements.
You have a similar problem here:
match /kitchens/{id=**} {
// Unauthenticated users can read kitchens
allow read: if true;
}
This allows anyone at at all to read the kitchens collection, and *all** of the nested documents in subcollections. Again, that's how the ** wildcard works.
If you want to restrict a match to just a single collection, don't use these "greedy" wildcards. Just use a normal one that only matches documents in the immediate subcollection:
match /kitchens/{id} {
// Unauthenticated users can read kitchens
allow read: if true;
}
You might want to go over the documentation about recursive wildcards.
I have a simple chat app on Cloud Firestore with the following security rule to only allow users the ability to send messages in a chat room if they "joined" the room in the app itself:
match /hangouts/{hangout}/messages/{message} {
allow create, write, update, read: if exists(/databases/$(database)/documents/users/{userId}/hangout/{hangout});
}
If the user has a hangoutID in their collection on Firestore then they should be allowed to send the message to that hangoutID. However, I'm still getting error messages:
Write at hangouts/ChIJPRVm2R7H54kRKLP2ttsuUko/messages/17225E70-B708-4033-AE5A-D0CBBD1BC69F failed: Missing or insufficient permissions.
I also have 2 other rules related to hangouts. Is it possible these are interfering?
match /hangouts/{hangout} {
allow read, update, write, create: if request.auth.uid != null;
}
match /hangouts/{hangout}/members/{userId} {
allow read: if request.auth.uid != null;
allow create, update, write, delete: if request.auth.uid == userId;
}
The first one is so people can create and see chatroom hangouts, and the second one is to allow people to join them.
What am I doing wrong? I'm a little new to this concept.
Thanks!!
It looks like you're not doing variable substitution correctly for document paths. In the match part, you use {curly braces} to indicate where the wildcards are, but in the path expression, you use the name of the wildcard like $(this).
/databases/$(database)/documents/users/$(userId)/hangout/$(hangout)
I have read the Firebase docs, and am wondering how Firebase rules affect a user's experience within an app. Let's say you can only access a group if you are apart of that group as per your Firestore rules. So would you instead of querying all groups where member.uid == auth.uid ( like you would without rules), would you just query the "groups" collection, and the user would only receive the groups that they are apart of?
Every time I read about Firestore rules, they pretty much give the same example, without giving a sample of what the user would see if they queried the database. If someone could point me in the right direction of this, or show how I could test something like this, that would be much appreciated.
So would you instead of querying all groups where member.uid == auth.uid ( like you would without rules), would you just query the "groups" collection, and the user would only receive the groups that they are apart of?
Firebase rules cannot filters data. You cannot have the client read an entire collection and then expect the rules to return a subset of the documents in that collection.
The reason for this is that listeners/observers are only validated once you attach them, after that they have access to the data. This is necessary in order to get the realtime update performance acceptable.
While you can't use rules to filter data, you can use them to validate queries. For an example of this, see the Firestore documentation on securely querying data:
service cloud.firestore {
match /databases/{database}/documents {
match /stories/{storyid} {
// Only the authenticated user who authored the document can read or write
allow read, write: if request.auth.uid == resource.data.author;
}
}
}
Which rejects this query:
// This query will fail
db.collection("stories").get()
But allows this one:
var user = firebase.auth().currentUser;
db.collection("stories").where("author", "==", user.uid).get()
You can see that we're essentially repeating the condition from the code in the rules. This allows you to securely expose a subset of the documents in a collection based on (for example) the user's UID.
The firebase rules are there just to prevent spam in your application and also other stuff.
If you use read:true and write:true then anyone can read and write to the database, that is why it is better to use:
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if request.auth.uid != null;
}
}
}
This way only authenticated users can see the data that is being retrieved from the database or can send data to the database.
If you have a users in a group and you used this:
service cloud.firestore {
match /databases/{database}/documents {
// Match any document in the 'groups' collection
match /groups/{group} {
allow read, write: if true;
}
}
}
It means all members of that group can read from that database, so if you are in that group, then the data will appear in your application.
more info here:
https://firebase.google.com/docs/firestore/security/rules-structure