在 Firebase Cloud Storage 安全性規則中使用條件

本指南以「瞭解 Firebase 安全性規則語言的核心語法」指南為基礎,說明如何在 Cloud Storage 的 Firebase 安全性規則中新增條件。

Cloud Storage 安全性規則的主要構成要素為條件。條件是一種布林值運算式,可判定是否應允許或拒絕特定作業。如果是基本規則,使用 truefalse 常值做為條件的最佳方式。但 Cloud Storage 語言適用的 Firebase 安全性規則 可讓您編寫更複雜的條件,進而:

  • 檢查使用者驗證
  • 驗證傳入的資料

驗證機制

Cloud Storage 的 Firebase 安全性規則可與 Firebase 驗證整合,為 Cloud Storage 提供強大的使用者驗證機制。這樣就能依據 Firebase 驗證權杖的要求,進行精細的存取權控管。

已驗證的使用者對 Cloud Storage 執行要求時,系統會在 request.auth 變數中填入使用者的 uid (request.auth.uid) 和 Firebase 驗證 JWT (request.auth.token) 的憑證附加資訊。

此外,使用自訂驗證時,request.auth.token 欄位會顯示其他憑證附加資訊。

當未經驗證的使用者執行要求時,request.auth 變數為 null

使用這些資料時,您可以透過幾種常見方式使用驗證機制保護檔案:

  • 公開:忽略 request.auth
  • 已驗證的私密性:檢查 request.auth 不是 null
  • 使用者不公開:檢查 request.auth.uid 是否等於路徑 uid
  • 群組不公開:檢查自訂權杖的憑證附加資訊是否與所選憑證附加資訊相符,或讀取檔案中繼資料,瞭解中繼資料欄位是否存在

公開

任何未考量 request.auth 情境的規則都可以視為 public 規則,因為不會將使用者的驗證情境納入考量。這些規則非常適合顯示公開資料,例如遊戲資產、音效檔案或其他靜態內容。

// Anyone to read a public image if the file is less than 100kB
// Anyone can upload a public file ending in '.txt'
match /public/{imageId} {
  allow read: if resource.size < 100 * 1024;
  allow write: if imageId.matches(".*\\.txt");
}

經過驗證的私人憑證

在某些情況下,您可能希望應用程式的所有已驗證使用者都能查看資料,但不想讓未經驗證的使用者查看。對所有未經驗證的使用者來說,request.auth 變數都是 null,所以您只需要檢查 request.auth 變數即可要求驗證:

// Require authentication on all internal image reads
match /internal/{imageId} {
  allow read: if request.auth != null;
}

使用者私人

到目前為止,request.auth 最常見的用途是向個別使用者授予其檔案的精細權限,從上傳個人資料相片到讀取私人文件。

由於 Cloud Storage 中的檔案具有完整的檔案「路徑」,因此只要讓使用者控管檔案,就必須是檔案名稱前置碼 (例如使用者的 uid) 中不重複的使用者識別資訊,在評估規則時可加以檢查:

// Only a user can upload their profile picture, but anyone can view it
match /users/{userId}/profilePicture.png {
  allow read;
  allow write: if request.auth.uid == userId;
}

群組私人

另一個同樣常見的用途是允許群組對物件的權限,例如允許多個團隊成員協作共用文件。以下是執行此動作的幾種方法:

  • 建立 Firebase 驗證自訂權杖,其中包含群組成員的其他相關資訊 (例如群組 ID)
  • 檔案中繼資料中加入群組資訊 (例如群組 ID 或授權 uid 的清單)

將這類資料儲存在權杖或檔案中繼資料後,即可在規則內參照該資料:

// Allow reads if the group ID in your token matches the file metadata's `owner` property
// Allow writes if the group ID is in the user's custom token
match /files/{groupId}/{fileName} {
  allow read: if resource.metadata.owner == request.auth.token.groupId;
  allow write: if request.auth.token.groupId == groupId;
}

要求評估

系統會使用傳送至 Cloud Storage 的 request,評估上傳、下載、中繼資料變更和刪除作業。除了上述 request.auth 物件中使用者的專屬 ID 和 Firebase 驗證酬載外,request 變數也包含執行要求的檔案路徑、接收要求的時間,以及如果要求是寫入,新的 resource 值。

request 物件也包含使用者專屬 ID 和 request.auth 物件中的 Firebase 驗證酬載,我們會在此文件的以使用者為基礎的安全性一節進一步說明。

以下是 request 物件中的完整屬性清單:

屬性 類型 說明
auth map<string, string> 使用者登入時,請提供 uid、使用者的專屬 ID 和 token (Firebase 驗證 JWT 憑證附加資訊)。否則為 null
params map<string, string> 包含要求查詢參數的地圖。
path 路徑 path,代表要求執行要求的路徑。
resource map<string, string> 新資源值,只會顯示在 write 要求中。
time 時間戳記 代表要求評估要求的伺服器時間的時間戳記。

資源評估

評估規則時,您可能也會想評估要上傳、下載、修改或刪除檔案的中繼資料。這可讓您建立複雜且強大的規則,例如只允許上傳特定內容類型的檔案,或僅允許刪除超過特定大小的檔案。

Cloud Storage 的 Firebase 安全性規則在 resource 物件中提供檔案中繼資料,其中包含 Cloud Storage 物件中呈現的中繼資料鍵/值組合。您可以在 readwrite 要求上檢查這些屬性,確保資料完整性。

針對 write 要求 (例如上傳、中繼資料更新和刪除),除了 resource 物件 (含有要求路徑中目前檔案的檔案中繼資料) 以外,您也可以使用 request.resource 物件,其中包含在允許寫入的情況下要寫入的部分檔案中繼資料。您可以使用這兩個值來確保資料完整性或強制執行應用程式限制,例如檔案類型或大小。

以下是 resource 物件中的完整屬性清單:

屬性 類型 說明
name 字串 物件全名
bucket 字串 這個物件所在值區名稱。
generation int 這個物件的Google Cloud Storage 物件產生
metageneration int 這個物件的 Google Cloud Storage 物件中繼產生
size int 物件大小 (以位元組為單位)。
timeCreated 時間戳記 代表物件的建立時間的時間戳記。
updated 時間戳記 代表物件上次更新時間的時間戳記。
md5Hash 字串 物件的 MD5 雜湊。
crc32c 字串 物件的 crc32c 雜湊。
etag 字串 與這個物件相關聯的 ETag。
contentDisposition 字串 與此物件相關聯的內容配置。
contentEncoding 字串 與此物件相關聯的內容編碼。
contentLanguage 字串 與這個物件相關聯的內容語言。
contentType 字串 與這個物件相關聯的內容類型。
metadata map<string, string> 開發人員指定的其他自訂中繼資料鍵/值組合。

request.resource 包含以上所有項目,但 generationmetagenerationetagtimeCreatedupdated 除外。

運用 Cloud Firestore 提升效能

您可以透過 Cloud Firestore 存取文件以評估其他授權條件。

使用 firestore.get()firestore.exists() 函式,您的安全性規則可以依據 Cloud Firestore 中的文件評估傳入要求。firestore.get()firestore.exists() 函式都會預期完整的文件路徑。使用變數建構 firestore.get()firestore.exists() 的路徑時,您必須使用 $(variable) 語法明確逸出變數。

在下方的範例中,我們會看到一項規則,限制只有特定俱樂部的成員能讀取檔案。

service firebase.storage {
  match /b/{bucket}/o {
    match /users/{club}/files/{fileId} {
      allow read: if club in
        firestore.get(/databases/(default)/documents/users/$(request.auth.id)).memberships
    }
  }
}
在下個範例中,只有好友的好友可以看到他們的相片。
service firebase.storage {
  match /b/{bucket}/o {
    match /users/{userId}/photos/{fileId} {
      allow read: if
        firestore.exists(/databases/(default)/documents/users/$(userId)/friends/$(request.auth.id))
    }
  }
}

建立並儲存第一個使用這些 Cloud Firestore 函式的 Cloud Storage 安全性規則後,您會在 Firebase 控制台或 Firebase CLI 看到提示,啟用連線這兩項產品的權限。

如要停用這項功能,請按照「管理及部署 Firebase 安全性規則」一文的說明移除 IAM 角色。

驗證資料

您也可以使用 Cloud Storage 的 Firebase 安全性規則驗證資料,包括驗證檔案名稱與路徑,以及檔案中繼資料屬性 (例如 contentTypesize)。

service firebase.storage {
  match /b/{bucket}/o {
    match /images/{imageId} {
      // Only allow uploads of any image file that's less than 5MB
      allow write: if request.resource.size < 5 * 1024 * 1024
                   && request.resource.contentType.matches('image/.*');
    }
  }
}

自訂函式

隨著 Firebase 安全性規則變得更加複雜,您可能會想要將多組條件納入函式中,以便在規則集內重複使用。安全性規則支援自訂函式。自訂函式的語法有點像 JavaScript,但 Firebase 安全性規則函式是以網域特定語言編寫,有一些重要限制:

  • 函式只能含有單一 return 陳述式。且不得包含任何額外的邏輯。例如無法執行迴圈或呼叫外部服務。
  • 函式可以從定義範圍內自動存取函式和變數。舉例來說,在 service firebase.storage 範圍中定義的函式可存取 resource 變數;如果是 Cloud Firestore,則僅限 Cloud Firestore 內建函式,例如 get()exists()
  • 函式可能會呼叫其他函式,但可能不會重複。呼叫堆疊總深度上限為 10。
  • rules2 版本中,函式可以使用 let 關鍵字定義變數。函式可以擁有任意數量的繫結,但結尾必須是傳回陳述式。

function 關鍵字定義函式,且必須使用零個或多個引數。例如,您可能想要將上述範例中使用的兩種條件類型合併為單一函式:

service firebase.storage {
  match /b/{bucket}/o {
    // True if the user is signed in or the requested data is 'public'
    function signedInOrPublic() {
      return request.auth.uid != null || resource.data.visibility == 'public';
    }
    match /images/{imageId} {
      allow read, write: if signedInOrPublic();
    }
    match /mp3s/{mp3Ids} {
      allow read: if signedInOrPublic();
    }
  }
}

在 Firebase 安全性規則中使用函式讓規則隨著規則的複雜度增加而更易於維護。

後續步驟

在討論條件之後,您對規則的瞭解更加精細,就可以開始:

瞭解如何處理核心用途,並學習開發、測試及部署規則的工作流程: