firebase cloud storage restrict read of access token - firebase

I have an app that allows the user to upload images to Firebase Cloud Storage, stores the downloadUrl in Firestore DB, and read it (show it on the app) later on, using the downloadUrl, which includes the Access Token.
My cloud storage security rules restricts read / write to authenticated users only (as the sample here) but seems like anyone can access those images, outside of the app, by using the Access Token (via web browser for example).
So if someone gets the downloadUrl (stored in Firestore DB) he can access the images....
Am I missing something here?
How can I restrict access to those images from outside the app?

The download URL contains a download token which acts as a security measure to restrict access only to those who possess the token. The download token is created automatically whenever a file is uploaded to Cloud Storage for Firebase. It's a random UUIDv4, which makes the URL hard to guess.
There's no way that you can restrict that URL (not even through Firebase Storage Security Rules). It is always public. This is commonly known as a "public, unguessable URL". There is also a revoke option through the Firebase Console just in case the URL leaks.
The Security Rules will only apply to those URL that doesn't have the Access Token included e.g.: https://firebasestorage.googleapis.com/v0/b/<bucket>/o/<file>?alt=media.
If this URL will be accessed unauthenticated with these sample Security Rule applied:
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write: if request.auth != null;
}
}
}
You will be given an error like this:
{
"error": {
"code": 403,
"message": "Permission denied."
}
}
However, As I pointed out, you don't need to be concerned about this URL (with access token) as in practice it is very hard to guess but if you don't want to store a shareable public URL (which is with the Access Token) then I would suggest using the Signed URL provided by Google Cloud Storage.

Related

Firebase revoking download URL doesn't work

I am using firebase, react and react-native to develop an MVP app where users can upload image files and other users can retrieve them for viewing, and I am using firebase storage and the getDownloadURL() function.
I know that there are other ways of retrieving firebase storage files, but I want to use the downloadURL so that unauthenticated users may also view the images.
I know that downloadURL is public and access to files cannot be restricted even by firebase security rules.
Nonetheless, there is the revoke function where I can supposedly revoke the access token, i.e. the downloadURL. At the firebase console, I tried it out. It turns out that every time I revoke it, firebase generates a new one as replacement. More problematic is that I can still use the old (revoked) URL to access the image files. I checked out at the browser developer tool. The URL used by the browser was indeed the revoked URL. I used a new browser to ensure that the problem is not related to the cache. Even if I use a react-ative app, the same problem appears.
The image cannot be accessed only if I completely delete it from the firebase storage.
What is the problem here? Have I missed something?
I have looked up the firebase documentation and searched for similar issues on stackoverflow but cannot get an answer. Other people don't seem to have this problem.
The reason why you can still access the revoked urls is because in your firebase storage rules you have accepted reads for all users, whether authenticated or unauthenticated.
To prevent access to revoked urls, use the following in your firebase storage rules.
NB// This will require all users to be authenticated inorder to get the download url
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write: if request.auth != null;
}
}
}

Firebase Storage: serving content to anonymous signed users without token

I'm trying to serve an image hosted on Firebase Storage only to users that are logged in anonymously. Here is my storage rule:
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read: if request.auth.token.firebase.sign_in_provider == "anonymous";
}
}
}
But this doesn't work:
If I go to
https://firebasestorage.googleapis.com/v0/b/project.appspot.com/o/image.png (without the token in the url) it gives me 403
If I go to https://firebasestorage.googleapis.com/v0/b/project.appspot.com/o/image.png?token=tokenId it serves the image, but it serves it to anyone (logged, not logged, etc.)
Also tried with the default rule:
allow read: if request.auth != null;
Same thing. The simulator works fine but in production it just doesn't work. Am I forgetting anything here?
Thanks!
The second URL you show seems to be a download URL, which (by definition) give read-only access to the resource to anyone who has that URL. There is no way to protect access through security rules for download URLs.
The first URL only works when access through the Firebase SDK, or when you explicitly set up the Cloud Storage security on the bucket to allow public access, which would bypass Firebase security rules completely.
Firebase JavaScript SDK 9.5 just added methods to access the data through the SDK, so I recommend checking those out.

Deleting of an user is not preventing resource access

I am using Firebase Authentication to log in to my app and Firebase Storage to save some files.
The problem is that even I have defined such safety rules for storage:
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write: if request.auth!=null;
}
}
}
When I delete or disable a user from Authentication Firebase manual panel then this user still can add and remove files to/from our storage.
With above rules I wanted to prevent a user to read/write when he/she is not authenticated.
According to documentation:
https://firebase.google.com/docs/auth/admin/manage-sessions
Refresh tokens expire only when one of the following occurs: - The user is deleted
In theory, it should work. Why it is not? And how to fix it, any idea?
Firebase Authentication uses ID tokens that are valid for one hour. Until the user's existing token expires, they can continue to use that token to access resources. But they won't be able to get a new ID token, so it will take no longer than an hour before they lose full access.
If this is not good enough for your scenario, you'll want to take additional measures to protect the files.
For data in the database, the documentation shows how to detect ID token revocation in security rule. But this approach won't work for Cloud Storage, as you can't perform a lookup of the revoked tokens in there.
For Cloud Storage you could implement your own API to control access to the files, where you'd then perform the same lookup of revoked tokens. That's the only way I can think of to implement immediate revocation.

Static hosting Google Cloud Storage with Firebase security rules, custom domain and CloudFlare

Let's imagine I am trying to host two static files using Firestore Storage:
index.html
secret.html
I have set up a CNAME record on CloudFlare to point my custom domain to "c.storage.googleapis.com" and I have also verified the domain on Google Cloud. Following this guide: https://stackoverflow.com/a/56697604/7871178
The bucket created with Firestore Storage has the same name as my domain and the bucket permission has been set to "allUsers" with the role "Storage Legacy Object Reader". I am able to access both files: index.html and secret.html without any form of authentication (due to the bucket permissions I have setup).
How would I make the contents of index.html public for all users, but secret.html restricted to the Firebase Storage Security rules (for example only Firebase authenticated users)?
Is this even possible with my current setup, is it all files public or nothing public at all?
Once that you have setup up your domain, in order to restrict your files you can make use of the Firebase Storage security rules to secure your assets. For example the next rule will allow your index.html to be readed by everyone and your secret.html to restricted just to authenticated users:
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /index.html {
allow read: if true;
}
match /secret.html {
allow read: if request.auth != null;
}
}
}
Just please be aware that since your bucket is public, due to the Storage Legacy Object Reader permissions,if a user somehow has the full bucket URL he will be able to access your assets directly, for example using the next url:
https://storage.googleapis.com/project.appspot.com/index.html #this will readable
https://storage.googleapis.com/project.appspot.com/secret.html #this also will be readable

Why don't the firebase rules work when I do a kreait / firebase-php operation with the service account?

sorry for being naive. I'm a little confused about authentication with kreait/firebase-php package. I defined some rules at firebase console, like:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow create,delete:if request.auth.uid == true;
}
}
}
The problem is that, even if I change the rule, the behavior of the services(CRUD) will work the same way. It appears that the JSON document (service account downloaded from google) has all permissions or does not require firebase rules.
In my case, I want to restrict access and database operations to users where they don't have authentication and permissions.
I am currently checking the email / password. If the request is true, the method will return a token. After that, I persist the token in CACHE and delete it when the user logs out. With each request, the middleware checks whether the token exists in CACHE. That way, it will only help me to restrict the user's access pages. How do I restrict the operation of the database (CRUD)? Thank you all!
This behavior is expected. Access to Firestore coming from code initialized with a service account will always bypass security rules. Security rules only apply to access from the web and mobile client SDKs, and the REST API when provided with a Firebase Authentication token.

Resources