Firebase存储安全规则和已上传文件的下载令牌

时间:2019-06-05 15:08:35

标签: firebase google-cloud-firestore firebase-storage

TLDR::这是关于从task.snapshot.ref.getDownloadURL()及其各自的downloadToken返回的URL的问题。它询问token的主要角色和功能是什么,以及是否需要由安全规则公开使用的文件。


我刚刚完成了有关将文件上传和下载到Firebase Storage的教程指南,该指南位于 https://firebase.google.com/docs/storage/web/upload-files Youtube tutorial from the Firebase official channel)上。

我正在其中一个Firebase Web应用程序(React + Firebase)中为博客部分构建内容管理系统。

我有一个组件,管理员可以选择一个图像并将其上传到Firebase存储桶,以显示在特定博客文章中。特定blogPost的所有图像都应位于特定blog-post-slug的文件夹中。

示例:

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

当管理员在<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);
      });
    }
  );
}

上面的代码返回downloadURL,该文件将保存到blogPost文档内的Firestore中。

downloadURL具有以下格式:

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

您会看到它带有“基本URL”: https://firebasestorage.googleapis.com/v0/b/MYFIREBASEAPP.appspot.com/o/some-slug%2FILE_NAME.jpg

basicURL 后面附加了以下GET参数:

alt=mediatoken=TOKEN_VALUE

我不知道会得到令牌,所以我现在正在测试其行为以了解更多信息。


允许读取具有存储行为:

service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write;
}}}
  • 当我访问basicURL时:
  • 我得到一个带有上传文件详细信息的对象:
{
  "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"
}
  • 当我访问basicURL?alt = media
  • 显示图像。

  • 当我访问basicURL时?alt = media&token = TOKEN_VALUE

  • 显示图像。

限制存储的行为:

service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write: if request.auth != null;
}}}
  • 当我访问basicURL时:
  • 我得到以下错误对象:
{
  "error": {
    "code": 403,
    "message": "Permission denied. Could not perform this operation"
  }
}
  • 当我访问basicURL?alt = media
  • 我得到了相同的错误对象:
{
  "error": {
    "code": 403,
    "message": "Permission denied. Could not perform this operation"
  }
}
  • 当我访问basicURL?alt = media&token = TOKEN_VALUE
  • 显示图像。

结论和问题

在我看来,安全规则allow read: if request.auth != null;应该已经阻止了对未授权用户的任何读取,但是使用TOKEN参数,即使没有auth对象的请求也可以访问该文件(注意:运行上述测试时,我尚未登录。)

我知道问一个以上的问题不是最佳实践,但在这种情况下,我认为有必要:

问题1:

此令牌的主要作用是什么?为什么它取代了auth规则?

问题2:

我希望这些图像可以公开获得,因为博客部分适用于所有用户。我应将哪个URL保存到Firestore?

  • 选项1:允许所有人阅读,只需保存basicURL。

  • 选项2:限制读取并保存basicURL +令牌。

问题3:

要显示<image src="imgSrc">标签中的图像,我是否需要alt=media参数?还是以FILE_NAME结尾的basicURL足够好吗?

编辑:问题3答案:刚刚对其进行了测试,发现alt=media GET参数对于在<img>标签内显示图像是必需的。


注意::如果您上传相同的文件并替换旧文件,则每次都会得到一个不同的token,而旧的token则无效。

2 个答案:

答案 0 :(得分:0)

    service firebase.storage {
      match /b/{bucket}/o {
        match /{allPaths=**} {
          allow read, write;
        }
      }
    }

这是Firebase存储的安全权限。所有类型的数据(图像,视频等)

答案 1 :(得分:0)

不幸的是,https://firebase.google.com/docs/storage/web/create-reference中描述的方法没有向请求和存储规则添加授权数据,例如

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

返回permission denied, 使用AngularFireStorage模块可以解决此问题。

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