Firebase Storage security rules and download tokens for uploaded files
Asked Answered
D

3

10

TLDR: This is a question about the URL returned from the task.snapshot.ref.getDownloadURL() and its respective downloadToken. It asks about what is the main role and functionality of the token and if it's necessary for files that will be publicly available by security rules.


I've just finished the tutorial guides about uploading and downloading files to Firebase Storage found on https://firebase.google.com/docs/storage/web/upload-files and also on this Youtube tutorial from the Firebase official channel.

I'm building a Content Management System for a blog section in one of my Firebase web apps (React + Firebase).

I have a component that lets the admin pick an image and upload it to Firebase Storage Bucket to be displayed in a specific blog post. All images to a specific blogPost should be inside a folder for the specific blog-post-slug.

Example:

//bucket/some-blog-post-slug/image1.jpg

Code that runs whenever the admin selected a new file on the <input type='file'/>:

function onFileSelect(e) {
  const file = e.target.files[0];
  const storageRef = firebase.storage().ref('some-slug/' + file.name);
  const task = storageRef.put(file);
  task.on('state_changed',
    function progress(snapshot) {
      setPercent((snapshot.bytesTransferred / snapshot.totalBytes) * 100);
    },
    function error(err) {
      console.log(err);
    },
    function complete() {
      console.log('Upload complete!');
      task.snapshot.ref.getDownloadURL().then(function(downloadURL) {
        console.log('File available at', downloadURL);
        props.changeImageSrc(downloadURL);
      });
    }
  );
}

The code above returns the downloadURL that will be saved to the Firestore inside the blogPost document.

The downloadURL has the following format:

https://firebasestorage.googleapis.com/v0/b/MYFIREBASEAPP.appspot.com/o/some-slug%2FILE_NAME.jpg?alt=media&token=TOKEN_VALUE

You can see that it comes with a "basic URL": https://firebasestorage.googleapis.com/v0/b/MYFIREBASEAPP.appspot.com/o/some-slug%2FILE_NAME.jpg

And the basicURL is appended with the following GET parameters:

alt=media and token=TOKEN_VALUE

I was not aware that I would be getting a token, so I'm now testing its behavior to know more about it.


BEHAVIOR WITH STORAGE READS ALLOWED:

service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write;
}}}
  • When I access the basicURL:
  • I get an object in response with the uploaded file details:
{
  "name": "some-slug/FILE_NAME.jpg",
  "bucket": "MYBUCKET",
  "generation": "GENERATION_NUMBER",
  "metageneration": "1",
  "contentType": "image/jpeg",
  "timeCreated": "2019-06-05T13:53:57.070Z",
  "updated": "2019-06-05T13:53:57.070Z",
  "storageClass": "STANDARD",
  "size": "815155",
  "md5Hash": "Mj4aCPs21NUNxXpKg1bHirFIO0A==",
  "contentEncoding": "identity",
  "contentDisposition": "inline; filename*=utf-8''FILE_NAME.jpg",
  "crc32c": "zhkQMQ==",
  "etag": "CKu4a1+u2+0ucI412CEAE=",
  "downloadTokens": "TOKEN_VALUE"
}
  • When I access the basicURL?alt=media
  • The image is displayed.

  • When I access the basicURL?alt=media&token=TOKEN_VALUE

  • The image is displayed.

BEHAVIOR WITH STORAGE READS RESTRICTED:

service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write: if request.auth != null;
}}}
  • When I access the basicURL:
  • I get the following error object:
{
  "error": {
    "code": 403,
    "message": "Permission denied. Could not perform this operation"
  }
}
  • When I access the basicURL?alt=media
  • I get the same error object:
{
  "error": {
    "code": 403,
    "message": "Permission denied. Could not perform this operation"
  }
}
  • When I access the basicURL?alt=media&token=TOKEN_VALUE
  • The image is displayed.

CONCLUSION AND QUESTIONS

It seems to me that the security rule allow read: if request.auth != null; should have blocked any reads from unauthorized users, but with the TOKEN parameter, the file is accessible even for requests without an auth object (Note: I wasn't logged in when I ran the tests above).

I know it's not the best practice to ask more than 1 question, but in this case I think it's necessary:

QUESTION 1:

What is this TOKEN mainly used for and why does it overried the auth rule?

QUESTION 2:

I want these images to be publicly availabe, as the blog section will be for all users. What URL should I save to Firestore?

  • Option 1: allow the reads for everyone and just save the basicURL.

  • Option 2: keep the reads restricted and save the basicURL + token.

QUESTION 3:

To display the images in <image src="imgSrc"> tags do I need the alt=media parameter? Or the basicURL ending in the FILE_NAME is good enough?

EDIT: QUESTION 3 ANSWER: Just tested it and found out that the alt=media GET parameter IS necessary to display the image inside an <img> tag.


NOTE: if you upload the same file and replace the old one, you'll get a different token each time and the older token becomes invalid.

Dance answered 5/6, 2019 at 15:8 Comment(1)
IMHO it is just very bad design decisions from googleKovar
P
0

Unfortunately, described method from https://firebase.google.com/docs/storage/web/create-reference does not add authorization data to request and storage rule like

service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write: if request.auth != null;
    }
  }
}

returns permission denied, using AngularFireStorage module resolves this issue.

import { AngularFireStorage } from '@angular/fire/storage';

 constructor(     
        private storage: AngularFireStorage
    ) {

    }
getFileUrl(path: string): Observable<string> {       
        const storageRef = this.storage.ref(path);
        return from(storageRef.getDownloadURL());
    }
Pretty answered 11/12, 2019 at 9:50 Comment(0)
R
0

Q1:

The token does not override the security rules, but is instead used for authentication (like a password managed by the storage).


Q2: You can either edit the rules so that everyone has access (globally).

Or decide for each file individually by making the file public instead of editing the firestore rules (as far as I understand it).


Read more: https://www.sentinelstand.com/article/guide-to-firebase-storage-download-urls-tokens

Reentry answered 26/8, 2023 at 8:12 Comment(0)
M
-1
    service firebase.storage {
      match /b/{bucket}/o {
        match /{allPaths=**} {
          allow read, write;
        }
      }
    }

This is security permissions for Firebase Storage. All Types of Data(Images, Video etc)

Mansoor answered 21/11, 2019 at 11:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.