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.
Related
How can I protect my uploaded document in Firebase?
For Example, this is my uploaded document URL:- https://firebasestorage.googleapis.com/v0/b/sapient-logic-368311.appspot.com/o/coder.JPEG?alt=media&token=55d1a727-956f-434b-bdad-a08b8ef133d0
Anyone can able to access and see my uploaded document by using this document URL.
How can I protect the uploaded document, then only authorized persons can access the uploaded document.
I want If anyone get document URL although Can't be access or see document
I want like this If anyone try to access my document using document URL:-
Or Can I make private bucket in Firebase ?
Anyone can able to access and see my uploaded document by using this document URL.
That's correct. And this is happening because you have shared the entire URL along with the token, which is not correct since the token should act as a security measure to restrict access only to those who possess the token.
So the best option that you have, would be to store such a URL in a database like Firestore or the Realtime Database and only allow access for reading the URL using security rules.
Remember, that token is created automatically whenever a file is uploaded to Cloud Storage for Firebase. So don't share that token with anyone.
You can write rules in it which help you who can read or write the data.
If a specific file is thought to be private, then no client normally should have access to the URL token.
If the case is the file URL was public and you want to make it private, you can modify the access token without being obligated to re-upload the file or changing its path.
I don't know what platform you are working on, but from the Python SDK, it can be done this way:
from firebase_admin import storage
from uuid import uuid4
bucket = storage.bucket()
blob = bucket.blob(path_to_file)
token = str(uuid4()) # Random ID
blob.metadata = {
"firebaseStorageDownloadTokens": token
}
blob.patch() # Updates changes
After this, the previous URL will be unusable and firestore will respond with the error you mention:
{
"error": {
"code": 403,
"message": "Permission denied."
}
}
Note that this solution applies for full-stack applications, where backend is what uploads the file to the Firebase Storage. If your project consists of the client connecting directly to the Firebase Storage, then you should take a read about the security rules and use them as a pseudo-backend.
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.
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;
}
}
}
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
I have image files in Firebase Storage what have Download URLs. I can access these just fine through the Download URLs.
I have set up a Google Cloud Platform CDN to point at my Firebase Storage bucket. When I try to access images in the bucket, I get an Access denied. response.
The instructions for setting up a GCP CDN pointed to a Bucket say to use the format:
https://[IP_ADDRESS]/static/[REGION]/[OBJECT_NAME]
I used the external load balancer IP Address, "us" for the region, and I have tried several things for [object_name].
The image file Object has a Download URL that looks like this:
https://firebasestorage.googleapis.com/v0/b/xxxx.appspot.com/o/-LmBkDlSNaSqtFI8AfFX%2Ffoo%2Fbar_-LmDYUM32YieXcpjm7jR.png?alt=media&token=61E4F4A8-99BF-495E-89F0-03BB565D0D8A
So, I tried using everything after /o/ as [object_name]
I tried the storage location path.
I tried URL encoding the slashes in both of these.
In all cases, I get the following:
<Error>
<Code>AccessDenied</Code>
<Message>Access denied.</Message>
<Details>Anonymous caller does not have storage.objects.get access to buttons-dev.appspot.com/static/us/-LmBkDlSNaSqtFI8AfFH/circles/circle_-LmDYUM32YieTcpjm7jR.png.
</Details>
</Error>
The permissions in Firebase Storage for the bucket are set to:
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write: if request.auth != null;
}
}
}
But, the Download URL should work for direct viewing of a particular file.
Also, I tried setting allow: read; for permissions, and nothing changed.
How can I access a Firebase Storage object in through it's Download URL and a Google Cloud Platform CDN?
The process is -
Setup a load balancer and enable CDN for it
Allow public access to the images you want to access via CDN or to the entire bucket. Instructions for it are in this link -
https://cloud.google.com/storage/docs/access-control/making-data-public
If you have a file in Firebase storage named foo/bar/image.png
then access it simply with http://[LOAD_BALANCER_IP_ADDRESS]/foo/bar/image.png
In your example, this will work
http://[LOAD_BALANCER_IP_ADDRESS]/-LmBkDlSNaSqtFI8AfFX%2Ffoo%2Fbar_-LmDYUM32YieXcpjm7jR.png
The base URL depends on what you have setup in Host and path rules. If you have only one backend setup then the above will work as-is.
Complete guide - https://cloud.google.com/load-balancing/docs/https/adding-backend-buckets-to-load-balancers