Firesbase Storage security rules allow update not working, can't prevent file over-write.
Example rule:
match /test/{userId}/{imageId} {
allow create: if request.auth.uid == userId;
allow update: if false;
}
Here I'm allowing authenticated users to only 'create' file, but not update/over-write.
Expected behavior: It shouldn't be allowed to update/over-write the existing file.
Actual behavior: When I'm again uploading a different file, but with same file name {over-writing the existing file}, then it's allowed to update/over-write.
How to prevent file update?
I tried to create the same structure as yours in Firebase Storage and turns out the rules are working for me.
I doubt if your structure is something like /test/{userId}/images/{imageId} which makes more sense if you are storing user generated content.
If the above if the case, then please update the rule to: /test/{userId}/images/{imageId}
If the issue still persists please share a screenshot of your Firebase Storage Directory structure.
Related
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
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 getting the URL with getDownloadURL after i put the url inside a img tag but i get this error
the rules of the storage are these:
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write: if request.auth != null;
}
}
}
So everybody should be able to read, so i can't understand :(
somebody can help?
This may be due to a missing permission. You need to check whether you have
firebase-storage#system.gserviceaccount.com
as a member with a "Storage Admin" role. If you don't have one, then add it. That would fix the issue.
Here are the steps on how you can check and add permissions.
Go to Cloud console
Navigate to Storage
Select your bucket then click show info panel.
You can also add the missing permission in the IAM & Admin if you want.
If it is not the problem then you are getting the error due to the some problem in the code, you may want to check it once more.
Can someone please explain why, while testing Firebase security rules the below two simulated writes which I think are essentially the same give different results?
Write1
Simulator location: /users/id123/state
Simulator data(JSON): {"data":"example}
Write2
Simulator location: /users/id123/
Simulator data(JSON): {"state":{"data":"example}}
Write 1 denies the wright at the "state":, ".write" line in the below rules.
Write 2 skips the "state":, ".write" line altogether.
This is an issue because I am updating multiple paths in one JSON update and its skipping rules.
Does anyone know why?
{
"rules":{
"users":{
"$userId":{
"state":{
".write":false,
".read":false
}
}
}
}
}
Your rule is for denying writes at all the locations at or deeper under your state node.
So any writes at
/Users/uid/state
And any rules under states
/Users/uid/state/foo/bar
Will be denied.
But if you write the data to
/Users/uid
The rule doesn't apply as it's not at or under the state node, it's a shallower node which doesn't have any rules stated. You can put your rules at
{
"rules":{
"users":{
"$userId":{
".write":false,
".read":false
}
}
}
}
The rules above will prevent writes for all the paths under your uid node. Also if you are using the REST api or the admin api, all rules will get bypassed
The REST API and the ADMIN API will both need a service account key file that only you will have. Thus they are secure. They'll give you admin access and will always bypass all security rules at all locations.
In the example you gave you are writing to different locations and firebase security will first look at the location you are writing at to check the rules.
Write2
Simulator location: /users/id123/
Simulator data(JSON): {"state":{"data":"example}}
Here you are writing to /users/id123/ and firebase will check that location for a write rule which in you case isn't present so it will use the default ".write":false.
For writing your rules you have to make sure to set them at the correct level.
Take a look at firebase security authorization (who can read/read) and also validation (what can be written).
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.