I want to allow links inside my application looking like:
mywebsite.com?u=nc27ri3ucfyinyh3
where nc27ri3ucfyinyh3 is a uuid, so the link can be sent to an anonymous user. The anonymous user should be able to view the page (database read), but it should also log to the database that they've viewed that link (database write).
As we get a warning when our firestore rules look like
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write;
}
}
}
as it is not secure.
Your security rules are defined as public, so anyone can steal, modify or delete data in your database
How should we handle the case of these anonymous users?
The first thing is that you could write a more restrictive set of rules than you have there (for example, restrict writes to just one collection (by changing the match /{document=**} line to something more restrictive (e.g. just the links collection or something). This, of course, still effectively allows anonymous users the full run of your database, but only within that collection.
Additionally, you can add validation to the incoming request via the request.resource object) -- its likely due to the anonymous nature of the user that you will still have a relatively insecure set of rules.
The data validation approach can look at both the current state of the database (in resource.data) as well as the contents of the incoming request (in request.resource). Here is the reference documentation for Resource and Request objects.
Here is an example rule that assumes these documents:
Exist in the /uuids collection
Are created by some other method (authenticated user, admin API, etc)
Only need to be fetched by ID, not queried as a set.
Only have 2 fields: content and visits
visits must be an integer, and is only allowed to be incremented
When the document is created, visits is initialized to zero.
I have not extensively tested these rules, only used the simulator to confirm they behave roughly as expected, I recommend you write extensive tests for any rules you intend to deploy. In particular, I am not certain about the behavior of the test for only being incremented when the document is under heavy contention.
rules_version = '2';
function notUpdating(field) {
return !(field in request.resource.data)
|| resource.data[field] == request.resource.data[field]
}
service cloud.firestore {
match /databases/{database}/documents {
match /uuids/{uuidValue} {
allow get: if true;
allow update: if (request.resource.data.keys().size() == 2 &&
notUpdating('content') &&
request.resource.data['visits'] == int(request.resource.data['visits']) &&
request.resource.data['visits'] > 0 &&
request.resource.data['visits'] == resource.data['visits'] + 1);
allow write: if false; // these 4 lines can also just be omitted
allow list: if false;
allow delete: if false;
allow create: if false;
}
}
}
This would allow you, for example, to ensure that only exactly the field you want is being touched, and only with valid data (e.g. positive integers or similar).
Remember -- the security rules are your only protection -- users can run arbitrary code against the database within those rules, not just code that you have given them. So, for example, if they can blanket read the collection, they can literally read the entire set of documents in that collection.
Alternatively, it might instead make sense to write an http, https, or callable cloud function that does exactly what you need -- register that the link has been used via a write, and then redirect or serve the necessary data itself. This gives you a lot more control over the specific write, but it does come with some added cost. The advantage here is that you wouldn't need to allow any public or open access to the database at all.
Cloud functions can also be served off of mywebsite.com if that web site is hosted on Firebase Hosting, via rewrite rules.
Related
I want unlogged users of my reactjs webapp to be able to read only "business profile collection".
I have the following db structure.
And the following rules:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read : if true;
allow write: if request.auth != null;
}
}
}
I am quite new to the firestore rules, i have multiple ways and this is the only one that worked for me.
The user Dharmaraj previously mentioned that your rules are allowing any user to read and write to any collection of the database, something you can validate using the rules playground. If that is the desired behavior, then you can ignore these alerts.
However, you said you wanted unlogged users of your app to be able to read only “business profile collections”. You can read the Production-ready rules and its sections, then use the one that is best for you. The way I see it, you should read and use the Attribute-based and Role-based access section and finish with something like this:
service cloud.firestore {
match /databases/{database}/documents {
// For attribute-based access control, Check a boolean `admin` attribute
allow write: if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.admin == true;
allow read: true;
// Alternatively, for role-based access, assign specific roles to users
match /some_collection/{document} {
allow read: if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role == "Reader"
allow write: if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role == "Writer"
}
}
}
Although, you might want to check them and read them carefully to see if any other option is more suitable for you. I will add the Security Rules language that is needed to understand what your rules are doing and how to Fix insecure rules.
I'm creating a web application using the Firebase Cloud Firestore, and I would like to write the security rules I've imagined, but I can't find any better documentation on this subject, it's always simple things, like check if the user is signed in.
So what I want to do is to check if the article that the client wants to read has a property called public, set to true. And maybe I can check the source of the request, to be sure it comes from my website's url ? I would like to find a solution to allow read without needing the user to sign-in , but also with a minimum of security.
And is it possible to return true if the property is undefined ? I would like to set the article public by default, but i don't know how to do it.
As Frank said, you cannot restrict access to Firestore from a specific domain. However and because you use some api key to call your firebase resources, you can restrict the use of this key to specific domain. You can do this by going to the GCP credentials page --> the API key you want to restrict. From there you can retrict how this key is used to websites, apps ...etc.
For you other question about checking if artice has public property, that can be done easily by use of security rules like:
service cloud.firestore {
match /databases/{database}/documents {
match /articles/{articleId} {
// I chose visibility as a prop here but it can be anything
allow read: if resource.data.visibility == "public"
}
}
}
To set the article as public by default you can do that from your client app when you create the article document itself by setting a property let's say "visibility" to public. something like this in your app:
// Add a new document with a generated id.
db.collection("articles").add({
visibility: "public"
...
})
I'm not sure if you can whitelist only your domain, but you can do pretty much everything you need with security rules
So what I want to do is to check if the article that the client wants to read has a property called public, set to true
allow read: if resource.data.yourPropertyName == "public"
I would like to find a solution to allow read without needing the user to sign-in
allow read: if true;
Keep in mind that those are not supposed to be used as filters, they are supposed to control who can write/read stuff
I am just starting in firebase with databases and I have a problem, I am making a page about an online store where I do not want there to be any kind of authentication, I just want the products to be seen with a brief description, but the problem is that every day a firebase email arrives telling me that my rules are insecure, so I wanted to know how to set my rules so that they are safe and only the information is displayed on my own page.
These are the rules I currently use:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow write: if false; allow read: if true;
}
}
}
I understand that they are open to all people, my idea is that at the moment it does not save any type of sensitive information in my database, I just want to save product data about my store, such as the price or a description avoiding all sensitive information. , my object is to return the safe rules and to be able to see the data from my page without any type of authentication for it.
I'll wager that google would consider the rules sufficiently locked down if the specific collection path was spelled out....
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /Put_Your_Product_Collection_Path_Here/{productDoc=**} {
allow write: if false; allow read: if true;
}
}
}
If you have other collections, be sure to secure those, too.
When I open my Firebase Storage bucket and go to files I have the following folder structure:
/<environment>/reports/<userId>/
I want that only the authenticated userId is allowed to read, write the reports in that user folder.
I tried the following, but it gives me the message that access is denied. What am I doing wrong? I copied almost one-to-one the example from the docs.
// Grants a user access to a node matching their user ID
service firebase.storage {
match /b/{bucket}/o {
// Files look like: "<ENVIRONMENT>/reports/<UID>/path/to/file.txt"
match /production/reports/{userId}/{allPaths=**} {
allow read, write: if request.auth.uid == userId;
}
}
}
You can use the rules simulator in the Firebase Storage Console to test your rules. I've tested yours and they seem to be working OK:
You'll need to remember to specify a final node name such as /production/reports/{userId}/test in order for the rule to work, as it will match the storage bucket filename rather than the parent directory due to /{allPaths=**}.
I'm sure I'm missing something wrt Firebase Storage rules, but I've done the following:
STEP 1
Firstly I set the following Firebase Storage rule:
service firebase.storage {
match /b/{bucket}/o {
match /items/{dev_key}/{perm_id}/{file_name} {
allow write: if request.auth.uid == dev_id;
allow read: if request.auth.token.permId == perm_id;
}
}
}
I expected only signed in users with a custom claim permId matching the relevant location to be able to download the file, allow read: if request.auth.token.permId == perm_id;.
So, I then set a custom claim in Cloud Functions on a user as follows:
STEP 2
admin.auth().setCustomUserClaims(uid, {permId: '1'}).then(() => {
// send off some triggers to let user know the download is coming
admin.database().ref(`collection/${uid}/${itemId}`).update({
downloadReady: true
});
});
Then I signed the user out and signed back in again... which set the custom claims.
I checked that they were set in Cloud Functions as follows:
STEP 3
admin.auth().verifyIdToken(idToken).then((claims) => {
console.log("--------------claims -------------");
console.log(JSON.stringify(claims));
});
And I saw in the claims string... permID: "1"
On the client side I then requested a downloadURL (here is hopefully where I'm going wrong)... I expected this to not be the public download url but rather the download url that the Firebase Storage security rules will check:
STEP 4
var pathReference = storage.ref('items/<some-key>/1/Item-1');
pathReference.getDownloadURL()
.then((url)=>{
console.log("url: ", url);
})
The url I received from this call gave me this link
https://firebasestorage.googleapis.com/v0/b/emiru84-games.appspot.com/o/games%2FcfaoVuEdJqOWDi9oeaLLphXl0E82%2F1%2FGame-1?alt=media&token=45653143-924a-4a7e-b51d-00774d8986a0
(a tiny little image I use for testing)
So far so good, the user with the correct claim was able to view this image
I then repeated step 2, logout/login again, except this time with a permId of "0". I expected the url generated previously to no longer work since my user no longer had the correct custom claim... and the bucket location was still at the same location (bucket/dev_key/1/filename) but it still worked.
If I repeated step 4 I got a new url, which then gave the appropriate 403 error response. However the old url still worked (I guess as long as the token parameter is tacked on). Is this expected, if so, I'm not sure I understand how the Storage security rules make a difference if the download url is public anyway?
Any help clearing my foggy brain would be appreciated.
The download URL in Cloud Storage for Firebase is always publicly readable. It is not affected by security rules.
If you don't want to allow public access to a file, you can revoke its download URL.