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

本指南將延續「瞭解 Firebase Security Rules 語言的核心語法」指南,說明如何在 Cloud StorageFirebase Security Rules 中加入條件。

Cloud Storage Security Rules 的主要建構區塊是條件。條件是布林值運算式,可決定是否應允許或拒絕特定作業。對於基本規則,使用 truefalse 常值做為條件非常合適。不過,Cloud Storage 語言的 Firebase Security Rules 可讓您編寫更複雜的條件,以便:

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

驗證

Cloud Storage 適用的 Firebase Security RulesFirebase Authentication 整合,可為 Cloud Storage 提供強大的使用者驗證功能。這可讓您根據 Firebase Authentication 權杖的宣告,實施精細的存取權控管。

當經過驗證的使用者對 Cloud Storage 執行要求時,request.auth 變數會填入使用者的 uid (request.auth.uid),以及 Firebase Authentication 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 Authentication 自訂權杖,其中包含群組成員的其他資訊 (例如群組 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 Storagerequest 來評估上傳、下載、中繼資料變更和刪除作業。除了使用者不重複 ID 和 request.auth 物件中的 Firebase Authentication 酬載 (如上所述),request 變數還包含執行要求的檔案路徑、收到要求的時間,以及要求為寫入時的新 resource 值。

request 物件也會在 request.auth 物件中包含使用者的專屬 ID 和 Firebase Authentication 酬載,我們會在文件的「使用者層級安全性」一節進一步說明。

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

屬性 類型 說明
auth map<string, string> 使用者登入後,系統會提供 uid (使用者的專屬 ID) 和 token (Firebase Authentication JWT 憑證的對應項目)。否則為 null
params map<string, string> 包含要求查詢參數的對應項目。
path 路徑 path 代表要求執行的路徑。
resource map<string, string> 新的資源值,僅會出現在 write 要求中。
time 時間戳記 代表要求評估時間的時間戳記。

資源評估

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

Cloud StorageFirebase Security Rules 會在 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 Security Rules 後,系統會在 Firebase 控制台或 Firebase CLI 中提示您啟用權限,以便連結這兩項產品。

如要停用這項功能,請移除 IAM 角色,如「管理及部署 Firebase Security Rules」一文所述。

驗證資料

Cloud StorageFirebase Security Rules 也可用於資料驗證,包括驗證檔案名稱和路徑,以及檔案中繼資料屬性,例如 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 Security Rules 變得越來越複雜,您可能會想在函式中包裝一組條件,以便在規則集上重複使用。安全性規則支援自訂函式。自訂函式的語法有點類似 JavaScript,但 Firebase Security Rules 函式是使用特定領域語言編寫,因此有以下幾項重要限制:

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

函式會使用 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 Security Rules 中使用函式,可讓規則在複雜度增加時更容易維護。

後續步驟

在討論條件後,您對規則有了更深入的瞭解,並準備好:

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