安全性可能是應用程式開發中最複雜的部分, 在大部分應用程式中,開發人員必須建構並執行 會處理驗證 (使用者的身分) 和授權 (使用者可執行的操作)。
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>;
}
}
}
如果您使用集合群組查詢,則必須使用第 2 版,請參閱保護集合群組查詢。
重疊的比對聲明
文件可能會比對多個 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 }
}
}
}