安全性可能是應用程式開發中最複雜的部分, 在大部分應用程式中,開發人員必須建構並執行 會處理驗證 (使用者的身分) 和授權 (使用者可執行的操作)。
Firebase Security Rules 會移除中間 (伺服器) 層並讓您指定以路徑為基礎的 權限。使用這份指南 進一步瞭解如何將規則套用至傳入的要求。
選取產品即可進一步瞭解相關規則。
Cloud Firestore
基本結構
Cloud Firestore 和 Cloud Storage 中的 Firebase Security Rules 採用以下結構和 語法:
service <<name>> {
// Match the resource path.
match <<path>> {
// Allow the request if the following conditions are true.
allow <<methods>> : if <<condition>>
}
}
建立規則時,請務必瞭解以下重要概念:
- 要求:
allow
陳述式中叫用的方法或方法。這些 您允許執行的方法標準方法包括:get
、list
、create
、update
和delete
。read
和write
便利方法 對指定的資料庫或儲存空間路徑啟用廣泛的讀寫存取權。 - 路徑:資料庫或儲存空間位置,以 URI 路徑。
- 規則:
allow
陳述式,其中包含允許 傳回的結果。
安全性規則第 2 版
自 2019 年 5 月起,Firebase 安全性規則的第 2 版現已上線
廣告。規則第 2 版改變了遞迴性
萬用字元 {name=**}
。如果您打算使用第 2 版
使用集合群組查詢。您必須選擇加入
版本 2,確保安全性的第一行 rules_version = '2';
規則:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
比對路徑
所有比對陳述式都應指向文件,而非集合。配對
陳述式可以指向特定文件 (例如 match /cities/SF
),或使用萬用字元
指向指定路徑中的任何文件,如 match /cities/{city}
中所示。
在上述範例中,比對陳述式使用 {city}
萬用字元語法。
也就是說,這項規則適用於 cities
集合中的任何文件,例如
/cities/SF
或 /cities/NYC
。當比對陳述式中的 allow
運算式出現
city
變數會解析為城市文件名稱。
例如 SF
或 NYC
相符的子集合
Cloud Firestore 中的資料會按照文件集合整理,而 文件可能會透過子集合擴充階層。請務必 瞭解安全性規則與階層資料的互動方式。
假設 cities
集合中的每份文件都包含
landmarks
個子集合。安全性規則只會套用到相符的路徑,因此
cities
集合中定義的存取權控管設定不適用於
landmarks
個子集合。請改為編寫明確規則來控管存取權
子集合:
service cloud.firestore {
match /databases/{database}/documents {
match /cities/{city} {
allow read, write: if <condition>;
// Explicitly define rules for the 'landmarks' subcollection
match /landmarks/{landmark} {
allow read, write: if <condition>;
}
}
}
}
建立 match
陳述式的巢狀結構時,內部 match
陳述式的路徑一律為
相對於外部 match
陳述式的路徑。下列規則集
因此相同:
service cloud.firestore {
match /databases/{database}/documents {
match /cities/{city} {
match /landmarks/{landmark} {
allow read, write: if <condition>;
}
}
}
}
service cloud.firestore {
match /databases/{database}/documents {
match /cities/{city}/landmarks/{landmark} {
allow read, write: if <condition>;
}
}
}
遞迴萬用字元
如要將規則套用至任意深層階層,請使用
遞迴萬用字元語法 {name=**}
:
service cloud.firestore {
match /databases/{database}/documents {
// Matches any document in the cities collection as well as any document
// in a subcollection.
match /cities/{document=**} {
allow read, write: if <condition>;
}
}
}
使用遞迴萬用字元語法時,萬用字元變數會包含
整個相符路徑片段 (即使文件位於深層巢狀結構中)
子集合。例如,上述規則
位於 /cities/SF/landmarks/coit_tower
的文件,以及
document
變數為 SF/landmarks/coit_tower
。
不過請注意,遞迴萬用字元的行為取決於規則 版本。
版本 1
根據預設,安全性規則會使用第 1 版。在第 1 版中,遞迴萬用字元
比對一或多個路徑項目它們與空白路徑不相符
match /cities/{city}/{document=**}
與子集合中的文件相符,但
不在 cities
集合中,但與 match /cities/{document=**}
相符
cities
集合和子集合中的文件。
遞迴萬用字元必須放在比對陳述式的結尾。
版本 2
在第 2 版的安全性規則中,遞迴萬用字元符合零或多個路徑
項目。match/cities/{city}/{document=**}
與任一份文件相符
子集合和 cities
集合中的文件。
您必須在頂端新增 rules_version = '2';
,才能採用第 2 版
安全性規則:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Matches any document in the cities collection as well as any document
// in a subcollection.
match /cities/{city}/{document=**} {
allow read, write: if <condition>;
}
}
}
每個比對陳述式最多只能有一個遞迴萬用字元,但在版本中則 2,您可以將這個萬用字元放在比對陳述式中的任何位置。例如:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Matches any document in the songs collection group
match /{path=**}/songs/{song} {
allow read, write: if <condition>;
}
}
}
如果您使用集合群組查詢,請務必使用 請參閱「保護集合群組查詢」一節。
重疊的比對聲明
文件可能會比對多個 match
陳述式。在
如果有多個 allow
運算式符合同一個要求,則允許存取
如果「任一」條件為 true
:
service cloud.firestore {
match /databases/{database}/documents {
// Matches any document in the 'cities' collection.
match /cities/{city} {
allow read, write: if false;
}
// Matches any document in the 'cities' collection or subcollections.
match /cities/{document=**} {
allow read, write: if true;
}
}
}
在上述範例中,對於 cities
集合的所有讀取和寫入作業都將
但第二項規則一律為 true
,但第 1 項
則一律為 false
。
安全性規則限制
處理安全性規則時,請注意以下限制:
限制 | 說明 |
---|---|
每項要求的 exists() 、get() 和 getAfter() 呼叫數量上限 |
超過任一項限制都會引發權限遭拒的錯誤。 系統可能會快取部分的文件存取呼叫,已快取的呼叫不會計入限制中。 |
巢狀 match 陳述式深度上限 |
10 |
一組巢狀 match 陳述式中允許的路徑長度上限 (以路徑區段為單位) |
100 |
一組巢狀 match 陳述式中允許的路徑擷取變數數量上限 |
20 |
函式呼叫深度上限 | 20 |
函式引數數量上限 | 7 |
每個函式的 let 變數繫結上限 |
10 |
遞迴或循環函式呼叫的數量上限 | 0 (不允許) |
每個要求中經評估的運算式數量上限 | 1,000 個 |
規則集的大小上限 | 規則集必須遵守以下兩項大小限制:
|
Cloud Storage
基本結構
Cloud Firestore 和 Cloud Storage 中的 Firebase Security Rules 採用以下結構和 語法:
service <<name>> {
// Match the resource path.
match <<path>> {
// Allow the request if the following conditions are true.
allow <<methods>> : if <<condition>>
}
}
建立規則時,請務必瞭解以下重要概念:
- 要求:
allow
陳述式中叫用的方法或方法。這些 您允許執行的方法標準方法包括:get
、list
、create
、update
和delete
。read
和write
便利方法 對指定的資料庫或儲存空間路徑啟用廣泛的讀寫存取權。 - 路徑:資料庫或儲存空間位置,以 URI 路徑。
- 規則:
allow
陳述式,其中包含允許 傳回的結果。
比對路徑
Cloud Storage Security Rules match
存取用來存取檔案的檔案路徑
Cloud Storage。規則可以match
確切路徑或萬用字元路徑,以及
也可以建立巢狀結構如果沒有比對規則允許要求方法,或
條件評估為 false
,系統就會拒絕要求。
完全相符的結果
// Exact match for "images/profilePhoto.png" match /images/profilePhoto.png { allow write: if <condition>; } // Exact match for "images/croppedProfilePhoto.png" match /images/croppedProfilePhoto.png { allow write: if <other_condition>; }
巢狀比對
// Partial match for files that start with "images" match /images { // Exact match for "images/profilePhoto.png" match /profilePhoto.png { allow write: if <condition>; } // Exact match for "images/croppedProfilePhoto.png" match /croppedProfilePhoto.png { allow write: if <other_condition>; } }
萬用字元比對
規則也可以使用萬用字元來 match
模式。萬用字元是
具名變數,代表單一字串,例如
profilePhoto.png
,或是多個路徑區隔,例如
images/profilePhoto.png
。
建立萬用字元的方法是在萬用字元名稱前後加上大括號,例如:
{string}
。如要宣告多個片段萬用字元,請在=**
萬用字元名稱,例如 {path=**}
:
// Partial match for files that start with "images" match /images { // Exact match for "images/*" // e.g. images/profilePhoto.png is matched match /{imageId} { // This rule only matches a single path segment (*) // imageId is a string that contains the specific segment matched allow read: if <condition>; } // Exact match for "images/**" // e.g. images/users/user:12345/profilePhoto.png is matched // images/profilePhoto.png is also matched! match /{allImages=**} { // This rule matches one or more path segments (**) // allImages is a path that contains all segments matched allow read: if <other_condition>; } }
如果有多個規則與檔案相符,結果會是所有結果的 OR
評估規則也就是說,如果檔案符合的任何規則評估為 true
,
結果是 true
。
在上述規則中,檔案「images/profilePhoto.png」就能讀取
condition
或 other_condition
的值為 true,而檔案
「images/users/user:12345/profilePhoto.png」只有在結果為
other_condition
。
您可以在 match
提供檔案中參照萬用字元變數
名稱或路徑授權:
// Another way to restrict the name of a file match /images/{imageId} { allow read: if imageId == "profilePhoto.png"; }
Cloud Storage Security Rules 不會串聯,且系統只會評估 要求路徑與包含指定規則的路徑相符。
要求評估
上傳、下載、中繼資料變更和刪除等作業,
request
已匯款給 Cloud Storage。request
變數包含
執行要求的檔案路徑、要求執行要求的時間
如果要求是寫入,則新的 resource
值。HTTP 標頭
以及驗證狀態
request
物件也會包含使用者的專屬 ID,以及
request.auth
物件中的 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 Storage 的 Firebase Security Rules 提供 resource
中的檔案中繼資料
物件,其中包含出現在
Cloud Storage 物件。如要查看這些屬性,請前往 read
或
用於確保資料完整性的 write
要求。
針對 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
包含以上所有項目,但 generation
除外。
metageneration
、etag
、timeCreated
和updated
。
安全性規則限制
處理安全性規則時,請注意以下限制:
限制 | 詳細資料 |
---|---|
最大 firestore.exists() 和
每次要求 firestore.get() 次呼叫 |
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 } } } }
Realtime Database
基本結構
在 Realtime Database 中,Firebase Security Rules 包含類似 JavaScript 的運算式,包含在 JSON 文件。
它們的語法如下:
{
"rules": {
"<<path>>": {
// Allow the request if the condition for each method is true.
".read": <<condition>>,
".write": <<condition>>,
".validate": <<condition>>
}
}
}
規則包含三個基本元素:
- 路徑:資料庫位置。這會反映資料庫的 JSON 結構。
- 要求:以下是規則用來授予存取權的方法。
read
和write
規則授予廣泛的讀取和寫入權限,validate
規則則授予 做為次要驗證方式,根據傳入或現有的資料來授予存取權 資料。 - 條件:允許要求在評估為 true 時允許的條件。
規則套用到路徑的方式
在 Realtime Database 中,Rules 會以不可分割的形式套用,亦即位於 較高層級的父項節點會覆寫規則 更精細的子節點 較深節點的規則無法授予父項路徑的存取權。個人中心 無法修正或撤銷資料庫結構中更深層的存取權 您已經為其中一個父項路徑授予這個角色。
請參考下列規則:
{ "rules": { "foo": { // allows read to /foo/* ".read": "data.child('baz').val() === true", "bar": { // ignored, since read was allowed already ".read": false } } } }
這個安全性結構允許 /bar/
讀取
/foo/
包含具有 true
值的子項 baz
。
/foo/bar/
下的 ".read": false
規則沒有任何
因為子項路徑無法撤銷存取權。
雖然您不一定要提供直覺易用的功能,但這項重要功能是 允許實施非常複雜的存取權限 以最省力的方式執行這對以使用者為基礎的安全機制來說特別實用。
不過,.validate
規則不會串聯。所有驗證規則
都必須滿足階層的所有層級要求,才能允許寫入。
此外,由於規則不會套用到父項路徑,因此讀取或寫入 作業失敗時,如果沒有指定位置或父項規則 授予存取權限的位置即使每個受影響的子項路徑都可以存取 導致無法完整讀取請參考以下結構:
{ "rules": { "records": { "rec1": { ".read": true }, "rec2": { ".read": false } } } }
如果不瞭解規則會以不可分割的形式進行評估
擷取 /records/
路徑時,將傳回 rec1
但不含 rec2
。不過,實際結果會是錯誤:
JavaScript
var db = firebase.database(); db.ref("records").once("value", function(snap) { // success method is not called }, function(err) { // error callback triggered with PERMISSION_DENIED });
Objective-C
FIRDatabaseReference *ref = [[FIRDatabase database] reference]; [[_ref child:@"records"] observeSingleEventOfType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) { // success block is not called } withCancelBlock:^(NSError * _Nonnull error) { // cancel block triggered with PERMISSION_DENIED }];
Swift
var ref = FIRDatabase.database().reference() ref.child("records").observeSingleEventOfType(.Value, withBlock: { snapshot in // success block is not called }, withCancelBlock: { error in // cancel block triggered with PERMISSION_DENIED })
Java
FirebaseDatabase database = FirebaseDatabase.getInstance(); DatabaseReference ref = database.getReference("records"); ref.addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(DataSnapshot snapshot) { // success method is not called } @Override public void onCancelled(FirebaseError firebaseError) { // error callback triggered with PERMISSION_DENIED }); });
REST
curl https://docs-examples.firebaseio.com/rest/records/ # response returns a PERMISSION_DENIED error
由於 /records/
的讀取作業不可部分完成,而且沒有讀取規則授予 /records/
下所有資料的存取權,因此系統會擲回 PERMISSION_DENIED
錯誤。如果我們透過 Firebase 控制台的安全模擬工具評估這項規則,可以發現讀取作業遭到拒絕:
Attempt to read /records with auth=Success(null) / /records No .read rule allowed the operation. Read was denied.
沒有任何讀取規則允許存取 /records/
路徑,因此作業遭拒。請注意,系統並未評估 rec1
的規則,因為該規則不在我們要求的路徑中。如要擷取 rec1
,我們必須直接存取:
JavaScript
var db = firebase.database(); db.ref("records/rec1").once("value", function(snap) { // SUCCESS! }, function(err) { // error callback is not called });
Objective-C
FIRDatabaseReference *ref = [[FIRDatabase database] reference]; [[ref child:@"records/rec1"] observeSingleEventOfType:FEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) { // SUCCESS! }];
Swift
var ref = FIRDatabase.database().reference() ref.child("records/rec1").observeSingleEventOfType(.Value, withBlock: { snapshot in // SUCCESS! })
Java
FirebaseDatabase database = FirebaseDatabase.getInstance(); DatabaseReference ref = database.getReference("records/rec1"); ref.addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(DataSnapshot snapshot) { // SUCCESS! } @Override public void onCancelled(FirebaseError firebaseError) { // error callback is not called } });
REST
curl https://docs-examples.firebaseio.com/rest/records/rec1 # SUCCESS!
位置變數
Realtime Database Rules支援 $location
來比對路徑區隔在路徑前方使用 $
前置字串
來比對規則和路徑上所有的子節點
{
"rules": {
"rooms": {
// This rule applies to any child of /rooms/, the key for each room id
// is stored inside $room_id variable for reference
"$room_id": {
"topic": {
// The room's topic can be changed if the room id has "public" in it
".write": "$room_id.contains('public')"
}
}
}
}
}
您也可以使用 $variable
搭配常數路徑
。
{
"rules": {
"widget": {
// a widget can have a title or color attribute
"title": { ".validate": true },
"color": { ".validate": true },
// but no other child paths are allowed
// in this case, $other means any key excluding "title" and "color"
"$other": { ".validate": false }
}
}
}