如何使用允许列表保护在 Firebase 存储上上传的文件?

How to secure files upload on Firebase Storage, using an allow list?

Firebase Storage 有一个很好的示例,说明如何通过其安全规则功能保护文件上传:

rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    match /images {
      // Cascade read to any image type at any path
      match /{allImages=**} {
        allow read;
      }

      // Allow write files to the path "images/*", subject to the constraints:
      // 1) File is less than 5MB
      // 2) Content type is an image
      // 3) Uploaded content type matches existing content type
      // 4) File name (stored in imageId wildcard variable) is less than 32 characters
      match /{imageId} {
        allow write: if request.resource.size < 5 * 1024 * 1024
                    && request.resource.contentType.matches('image/.*')
                    && request.resource.contentType == resource.contentType
                    && imageId.size() < 32
      }
    }
  }
}

但是如果您想要允许更多文件(不仅仅是图像?)怎么办?

request.resource.contentType.matches('image/.*') 中的 matches 方法根据 Firebase Documentation 接受正则表达式,因此您可以这样做:

request.resource.contentType.matches('image/jpeg|image/png|image/gif')

如果您认为自己会得到一长串 mime-types,您可能应该开始在 Firebase 规则中使用自定义函数。这是上面的相同示例,带有自定义函数和更多 mime-types:

rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    match /images {
      // Cascade read to any image type at any path
      match /{allImages=**} {
        allow read;
      }

      // Allow write files to the path "images/*", subject to the constraints:
      // 1) File is less than 5MB
      // 2) Content type is an image
      // 3) Uploaded content type matches existing content type
      // 4) File name (stored in imageId wildcard variable) is less than 32 characters
      match /{imageId} {
        // True if the uploaded file is part of the allow-list
        function isAllowedFile(request) {
          // Mime-types list source: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition
          let allowList = "audio/aac|application/x-abiword|application/x-freearc|image/avif|video/x-msvideo|application/vnd.amazon.ebook|application/octet-stream|image/bmp|application/x-bzip|application/x-bzip2|application/x-cdf|application/x-csh|text/css|text/csv|application/msword|application/vnd.openxmlformats-officedocument.wordprocessingml.document|application/vnd.ms-fontobject|application/epub+zip|application/gzip|image/gif|image/vnd.microsoft.icon|text/calendar|image/jpeg|application/json|application/ld+json|audio/midi|audio/x-midi|audio/mpeg|video/mp4|video/mpeg|application/vnd.apple.installer+xml|application/vnd.oasis.opendocument.presentation|application/vnd.oasis.opendocument.spreadsheet|application/vnd.oasis.opendocument.text|audio/ogg|video/ogg|application/ogg|audio/opus|font/otf|image/png|application/pdf|application/x-httpd-php|application/vnd.ms-powerpoint|application/vnd.openxmlformats-officedocument.presentationml.presentation|application/vnd.rar|application/rtf|application/x-sh|image/svg+xml|application/x-tar|image/tiff|video/mp2t|font/ttf|text/plain|application/vnd.visio|audio/wav|audio/webm|video/webm|image/webp|font/woff|font/woff2|application/vnd.ms-excel|application/vnd.openxmlformats-officedocument.spreadsheetml.sheet|application/xml|application/vnd.mozilla.xul+xml|application/zip|video/3gpp|audio/3gpp|video/3gpp2|audio/3gpp2|application/x-7z-compressed";
          return request.resource.contentType.matches(allowList);
        }

        allow write: if request.resource.size < 5 * 1024 * 1024
                    && isAllowedFile(request)
                    && request.resource.contentType == resource.contentType
                    && imageId.size() < 32
      }
    }
  }
}