Firebase revoking download URL doesn't work - firebase

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;
}
}
}

Related

firebase cloud storage restrict read of access token

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.

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.

Setting Firebase Firestore to only allow read/write from a single Unity App

I've been looking for this answer for a while and honestly there isn't a lot of Firebase using Unity tutorials out that that gives a definitive answer.
I'm using Firestore as a database for an Unity app. My goal is to only allow that app to write to/read from that database. There is no user login, as the app has no use for it. Everything is working already as far as the reading and writing.
My first question is, do I even need Firebase Authentication for this? Or can the app only read/write if it has the associated GoogleServices.json (android) and PList (iOS) files in when it was built? I don't want unauthorized users to access my databse, and i'm unsure how secure just having the json and plist files are.
My second question is this. I have 2 data nodes in the database, one for reading and the other for writing to the general user of the app. I know how to set up the rules so that's the case. But I do want special execution, say I have a personal app that should be able to read the otherwise write-only node, and write to the otherwise read-only node.
Would I need Firebase Authentication then? I assume I have to make a read exception to use Authentication in the security rules.
Thanks in advance for your help.
Question1:
If you do not want unauthorized access to your database, you do need Firebase Auth. Luckily, Firebase Auth has a feature for people like you who don't need to have people log in to your app. It's called Anonymous Authentication, and it essentially works by you signing in users in the back ground without your app's user knowing it. With this, you can restrict database access so that a certain user would not be able to override someone else's data. You can read more about Anonymous Authentication with Unity here.
Question2:
I don't know if the situation you are describing like a admin case, where you as an admin of the app should be able to read/write to the database where normal users should not be allowed, but if that is the case you may use "Custom Claims". With custom claims, you are adding a special key-value pair to your auth token to then use it in security rules. You can read more about custom claims here.
thank you for the quick reply. I got the anonymous sign-in to work, and I think I set up the rules correctly.
I have a document in Firestore called 'organizations', and I want the any user that is signed-in to be able to read it and everything below it, but only write if he's user id is administrator. From the Firestore docs, I think I should set the rules like this:
match /databases/{database}/documents
{
match /organizations/{organization=**}
{
allow read: if request.auth != null;
allow write: if request.auth.uid == "administrator";
}
}
I have a function that runs at the beginning of the app that signs the user in anonymously, which I can confirm works since I can read the user when the task is completed.
However, something isn't working right in the rules.
The app can read the Firestore node whether or not I'm signed in or not. I can comment out the sign-in function, and it will still read the "organization" node. I'm not sure if signing in anonymously does anything at all. Maybe I'm signing in wrong?
Here is my sign-in function:
public async Task SignInAnon()
{
await FirestoreController.firebaseAuth.SignInAnonymouslyAsync().ContinueWith(task =>
{
if (task.IsCanceled)
{
Debug.LogError("SignInAnonymouslyAsync was canceled.");
return;
}
if (task.IsFaulted)
{
Debug.LogError("SignInAnonymouslyAsync encountered an error: " + task.Exception);
return;
}
GameManager.anonUser = task.Result;
Debug.Log("Firebase Signed-In as: " + GameManager.anonUser.UserId);
});
}
anonUser is a static variable initiated elsewhere in the code. Or can I not doing it this way?
EDIT: I'm an idiot. I signed in once and I didn't bother to sign the user out, so I'm treated as signed in even if I comment out the code. :P

Firebase Storage never fully private?

I have set rules for our Firebase Storage to only allow authenticated users to interact with it.
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /something/{allPaths=**} {
allow read, write: if request.auth.uid != null;
}
}
}
However, when I browse the details of the assets saved in the storage in the Firebase Console, I do find for each of these, a link to open these publicly (when I open the details of a file), for example:
https://firebasestorage.googleapis.com/v0/b/${my-project}.appspot.com/o/${my-file}?alt=media&token=49385ae5-4df6-44ef-a313-8d1f07b59111
My guess is that this link is the downloadURL and the public access is granted through the token.
Therefore I understand that the storage content is actually not fully private because even if I set strong rules and even if I personally and my apps don't generate download URL, Firebase still generate automatically such url for each file and therefore, even if token are hard to find, the content is in any case accessible online publicly.
Is my understanding correct?
Or the link I found in thee Firebase console isn't the download url but a temporary link respectively the token provided by the console as an expiration life?
Or is it possible to set the Firebase Storage Bucket to not generate any download links ever?
Anyone who has a downlaod URL with a valid token can access the content from anywhere. However, if you never expose that URL to anyone, it's essentially impossible for anyone to guess that URL, even if they know the path. The token is random and contains a tremendous amount of entropy.
If you're concerned about anyone guessing this URL with the correct token, you should also be concerned about two random atoms in the universe colliding with each other. (That's a slight exaggeration, but you can do the math if you assume that each character of a token is a valid hex digit.)
If you're still paranoid, and you don't want to generate any download URL at all, you will have to manually revoke the download URL tokens, and use an upload technique that doesn't also create a download URL, and never call getDownloadUrl on any file from a client app.

Resources