安全性規則的運作方式

安全性可能是應用程式開發中最複雜的部分, 在大部分應用程式中,開發人員必須建構並執行 會處理驗證 (使用者的身分) 和授權 (使用者可執行的操作)。

Firebase Security Rules 會移除中間 (伺服器) 層並讓您指定以路徑為基礎的 權限。使用這份指南 進一步瞭解如何將規則套用至傳入的要求。

選取產品即可進一步瞭解相關規則。

Cloud Firestore

基本結構

Cloud FirestoreCloud 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 陳述式中叫用的方法或方法。這些 您允許執行的方法標準方法包括:getlistcreateupdatedeletereadwrite 便利方法 對指定的資料庫或儲存空間路徑啟用廣泛的讀寫存取權。
  • 路徑:資料庫或儲存空間位置,以 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 變數會解析為城市文件名稱。 例如 SFNYC

相符的子集合

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() 呼叫數量上限
  • 單一文件要求和查詢要求的上限為 10 項。
  • 多文件讀取作業、交易和批次寫入作業的上限為 20 項。上述限制 (10 項呼叫) 同樣適用於各項作業。

    舉例來說,假設您建立的批次寫入要求含有 3 項寫入作業,而您的安全性規則會使用 2 項文件存取呼叫來驗證各項寫入作業。此時,各項寫入作業會使用 2 項存取呼叫 (上限為 10 項),批次寫入要求則會使用 6 項存取呼叫 (上限為 20 項)。

超過任一項限制都會引發權限遭拒的錯誤。

系統可能會快取部分的文件存取呼叫,已快取的呼叫不會計入限制中。

巢狀 match 陳述式深度上限 10
一組巢狀 match 陳述式中允許的路徑長度上限 (以路徑區段為單位) 100
一組巢狀 match 陳述式中允許的路徑擷取變數數量上限 20
函式呼叫深度上限 20
函式引數數量上限 7
每個函式的 let 變數繫結上限 10
遞迴或循環函式呼叫的數量上限 0 (不允許)
每個要求中經評估的運算式數量上限 1,000 個
規則集的大小上限 規則集必須遵守以下兩項大小限制:
  • 規則集文字來源的大小限制為 256 KB 透過 Firebase 控制台或 CLI 發布 firebase deploy
  • 針對結果產生的已編譯規則集大小,限制為 250 KB Firebase 處理來源並在 後端。

Cloud Storage

基本結構

Cloud FirestoreCloud 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 陳述式中叫用的方法或方法。這些 您允許執行的方法標準方法包括:getlistcreateupdatedeletereadwrite 便利方法 對指定的資料庫或儲存空間路徑啟用廣泛的讀寫存取權。
  • 路徑:資料庫或儲存空間位置,以 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」就能讀取 conditionother_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 Storagerequest 變數包含 執行要求的檔案路徑、要求執行要求的時間 如果要求是寫入,則新的 resource 值。HTTP 標頭 以及驗證狀態

request 物件也會包含使用者的專屬 ID,以及 request.auth 物件中的 Firebase Authentication 酬載, 如要進一步瞭解,請參閱驗證 一節。

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

屬性 類型 說明
auth map<string, string> 使用者登入時,請提供 uid、使用者的專屬 ID,以及 tokenFirebase Authentication JWT 憑證附加資訊的對應。否則, null
params map<string, string> 包含要求查詢參數的地圖。
path 路徑 path 代表要求目前所在的路徑 執行的所有工作
resource map<string, string> 新資源值,只會顯示在 write 要求中。
time 時間戳記 代表要求評估要求的伺服器時間的時間戳記。

資源評估

評估規則時,建議您評估檔案的中繼資料 上傳、下載、修改或刪除這可讓您 複雜且功能強大的規則,例如僅允許符合特定條件的檔案 要上傳的內容類型,或只上傳大於特定大小的檔案 已刪除。

Cloud StorageFirebase 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 除外。 metagenerationetagtimeCreatedupdated

安全性規則限制

處理安全性規則時,請注意以下限制:

限制 詳細資料
最大 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 結構。
  • 要求:以下是規則用來授予存取權的方法。readwrite 規則授予廣泛的讀取和寫入權限,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
注意:這項 Firebase 產品不適用於 App Clip 目標。
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
注意:這項 Firebase 產品不適用於 App Clip 目標。
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
注意:這項 Firebase 產品不適用於 App Clip 目標。
FIRDatabaseReference *ref = [[FIRDatabase database] reference];
[[ref child:@"records/rec1"] observeSingleEventOfType:FEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) {
    // SUCCESS!
}];
Swift
注意:這項 Firebase 產品不適用於 App Clip 目標。
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 }
      }
    }
  }