管理和部署 Firebase 安全规则

Firebase 为您提供了多种规则管理工具,每种工具在特定情况下都很有用,并且都使用相同的后端 Firebase Security Rules Management API。

无论使用哪种工具调用 Management API,该 API 都可以:

  • 注入规则来源:一组规则,通常是包含 Firebase 安全规则语句的代码文件。
  • 将注入的来源存储为不可变规则集
  • 跟踪版本中每个规则集的部署。启用了 Firebase 安全规则的服务会查找项目的版本,以评估针对受保护资源发出的每个请求。
  • 提供运行规则集的语法和语义测试的功能。

使用 Firebase CLI

使用 Firebase CLI,您可以上传本地来源和部署版本。使用 CLI 的 Firebase Local Emulator Suite,您可以对来源执行完整的本地测试。

使用 CLI,您可以借助应用代码对规则进行版本控制,并在现有的部署过程中部署规则。

生成配置文件

使用 Firebase CLI 配置 Firebase 项目时,您可在项目目录中创建 .rules 配置文件。使用以下命令开始配置 Firebase 项目:

Cloud Firestore

// Set up Firestore in your project directory, creates a .rules file
firebase init firestore

Realtime Database

// Set up Realtime Database in your project directory, creates a .rules file
firebase init database

Cloud Storage

// Set up Storage in your project directory, creates a .rules file
firebase init storage

修改和更新您的规则

直接在 .rules 配置文件中修改规则来源。确保您在 Firebase CLI 中进行的所有修改都体现在 Firebase 控制台中,或者您始终使用 Firebase 控制台或 Firebase CLI 进行更新。否则,您可能会覆盖在 Firebase 控制台中所做的所有更新。

测试您的更新

Local Emulator Suite 为所有启用了安全规则的产品提供模拟器。每个模拟器的安全规则引擎都会对规则进行语法和语义评估,因而超出了 Security Rules Management API 提供的语法测试的范围。

如果您使用的是 CLI,则该套件是进行 Firebase 安全规则测试的绝佳工具。使用 Local Emulator Suite 在本地测试您的更新,并确认您的应用规则表现出您期望的行为。

部署您的更新

更新并测试规则后,将来源部署到生产环境中。使用以下命令,可以选择单独部署规则,或者将其作为正常部署过程的一部分进行部署。

Cloud Firestore

// Deploy your .rules file
firebase deploy --only firestore:rules

Realtime Database

// Deploy your .rules file
firebase deploy --only database

Cloud Storage

// Deploy your .rules file
firebase deploy --only storage

使用 Firebase 控制台

您还可以通过 Firebase 控制台修改规则来源,并将其部署为版本。当您在 Firebase 控制台界面中进行修改时,执行的是语法测试;您可以使用规则测试平台进行语义测试。

修改和更新您的规则

  1. 打开 Firebase 控制台,然后选择您的项目。
  2. 然后从产品导航中选择 Realtime DatabaseCloud FirestoreStorage,再点击规则以导航到规则编辑器。
  3. 直接在编辑器中修改规则。

测试您的更新

除了在编辑器界面中测试语法之外,您还可以直接在 Firebase 控制台中使用规则测试平台通过项目的数据库和存储资源测试规则的语义行为。在规则编辑器中打开规则测试平台屏幕,修改设置,然后点击运行。在编辑器顶部查找确认消息。

部署您的更新

如果您的更新达到您满意的效果,请点击发布

使用 Admin SDK

您可以使用 Node.js 版 Admin SDK 规则集。通过以编程方式访问,您可以执行以下操作:

  • 实现自定义工具、脚本、信息中心和 CI/CD 流水线以管理规则。
  • 更轻松地管理多个 Firebase 项目的规则。

以编程方式更新规则时,需要避免意外更改应用的访问权限控制。在编写 Admin SDK 代码时,请特别注意安全问题,尤其是在更新或部署规则时。

另一个需要注意的重要事项是,Firebase 安全规则版本需要几分钟的时间才能完全传播。使用 Admin SDK 部署规则时,请确保避免出现以下竞态条件:应用立即依赖于部署尚未完成的规则。如果您的用例需要频繁更新访问权限控制规则,请考虑使用 Cloud Firestore 解决方案,该方案可有效避免频繁更新时出现争用的情况。

另请注意以下限制:

  • 规则在序列化后必须小于 256KiB 的 UTF-8 编码文本。
  • 一个项目最多只能有 2500 个已部署的规则集。达到上限后,您必须先删除一些旧规则集,然后才能创建新规则集。

创建和部署 Cloud Storage 或 Cloud Firestore 规则集

使用 Admin SDK 管理安全规则的典型工作流程包括三个独立的步骤:

  1. 创建规则文件来源(可选)
  2. 创建规则集
  3. 发布或部署新规则集

SDK 提供了一种方法,可将这几个步骤合并到针对 Cloud Storage 和 Cloud Firestore 安全规则的单个 API 调用中。例如:

    const source = `service cloud.firestore {
      match /databases/{database}/documents {
        match /carts/{cartID} {
          allow create: if request.auth != null && request.auth.uid == request.resource.data.ownerUID;
          allow read, update, delete: if request.auth != null && request.auth.uid == resource.data.ownerUID;
        }
      }
    }`;
    // Alternatively, load rules from a file
    // const fs = require('fs');
    // const source = fs.readFileSync('path/to/firestore.rules', 'utf8');

    await admin.securityRules().releaseFirestoreRulesetFromSource(source);

此模式同样适用于具有 releaseFirestoreRulesetFromSource() 的 Cloud Storage 规则。

或者,您可以将规则文件创建为内存对象,创建规则集,并单独部署此规则集以进一步控制这些事件。 例如:

    const rf = admin.securityRules().createRulesFileFromSource('firestore.rules', source);
    const rs = await admin.securityRules().createRuleset(rf);
    await admin.securityRules().releaseFirestoreRuleset(rs);

更新 Realtime Database 规则集

如需使用 Admin SDK 更新实时数据库规则集,请使用 admin.databasegetRules()setRules() 方法。您可以以 JSON 格式检索规则集,或者以包含注释的字符串形式检索规则集。

如需更新规则集,请使用以下语句:

    const source = `{
      "rules": {
        "scores": {
          ".indexOn": "score",
          "$uid": {
            ".read": "$uid == auth.uid",
            ".write": "$uid == auth.uid"
          }
        }
      }
    }`;
    await admin.database().setRules(source);

管理规则集

为便于管理大型规则集,Admin SDK 允许您使用 admin.securityRules().listRulesetMetadata 列出所有现有规则。例如:

    const allRulesets = [];
    let pageToken = null;
    while (true) {
      const result = await admin.securityRules().listRulesetMetadata(pageToken: pageToken);
      allRulesets.push(...result.rulesets);
      pageToken = result.nextPageToken;
      if (!pageToken) {
        break;
      }
    }

对于在一段时间内达到 2500 个规则集限制的大型部署,您可以创建逻辑,以删除固定的时间段内最旧的规则。例如,如需删除部署时间超过 30 天的所有规则集,请使用以下语句:

    const thirtyDays = new Date(Date.now() - THIRTY_DAYS_IN_MILLIS);
    const promises = [];
    allRulesets.forEach((rs) => {
      if (new Date(rs.createTime) < thirtyDays) {
        promises.push(admin.securityRules().deleteRuleset(rs.name));
      }
    });
    await Promise.all(promises);
    console.log(`Deleted ${promises.length} rulesets.`);

使用 REST API

上述工具非常适合各种工作流,但您可能希望使用 Management API 本身管理和部署 Firebase 安全规则。Management API 为您提供了最大的灵活性。

请注意,Firebase 安全规则版本需要几分钟时间才能完成部署。 使用 Management REST API 进行部署时,请确保避免出现以下竞态条件:您的应用立即依赖于部署尚未完成的规则。

另请注意以下限制:

  • 规则在序列化后必须小于 256KiB 的 UTF-8 编码文本。
  • 一个项目最多只能有 2500 个已部署的规则集。达到上限后,您必须先删除一些旧规则集,然后才能创建新规则集。

使用 REST 创建和部署 Cloud Storage 或 Cloud Firestore 规则集

本部分中的示例使用的是 Storage 规则(不过这些示例也适用于 Cloud Firestore 规则)。

这些示例还使用 cURL 进行 API 调用。设置和传递身份验证令牌的步骤已省略。您可以使用集成了参考文档的 API Explorer 试用此 API。

使用 Management API 创建和部署规则集的典型步骤如下:

  1. 创建规则文件来源
  2. 创建规则集
  3. 发布(部署)新规则集

假设您正在处理您的 secure_commerce Firebase 项目,而且想要部署锁定的 Cloud Storage 规则。您可以在 storage.rules 文件中实现这些规则。

service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write: if false;
    }
  }
}

现在,请为此文件生成 base64 编码的指纹。然后,您可以使用此文件中的来源填充通过 projects.rulesets.create REST 调用创建规则集所需的载荷。我们使用 cat 命令将 storage.rules 的内容插入 REST 载荷中。

curl -X POST -d '{
  "source": {
    {
      "files": [
        {
          "content": "' $(cat storage.rules) '",
          "name": "storage.rules",
          "fingerprint": <sha fingerprint>
        }
      ]
    }
  }
}' 'https://firebaserules.googleapis.com/v1/projects/secure_commerce/rulesets'

API 会返回验证响应和规则集名称,例如 projects/secure_commerce/rulesets/uuid123。如果规则集有效,则最后一步是在指定版本中部署新规则集。

curl -X POST -d '{
  "name": "projects/secure_commerce/releases/prod/v23   "  ,
  "rulesetName": "projects/secure_commerce/rulesets/uuid123",
}' 'https://firebaserules.googleapis.com/v1/projects/secure_commerce/releases'

使用 REST 更新 Realtime Database 规则集

Realtime Database 提供自己的 REST 接口来管理规则。请参阅使用 REST 管理 Firebase Realtime Database 规则

使用 REST 管理规则集

为了帮助管理大型规则部署,除了用于创建规则集和版本的 REST 方法外,Management API 还提供执行以下操作的方法:

  • 列出、获取和删除规则集
  • 列出、获取和删除规则版本

对于在一段时间内达到 2500 个规则集限制的大型部署,您可以创建逻辑,以删除固定的时间段内最旧的规则。例如,如需删除部署时间超过 30 天的所有规则集,您可以调用 projects.rulesets.list 方法,根据 createTime 键解析 Ruleset 对象的 JSON 列表,然后按 ruleset_id 对相应的规则集调用 project.rulesets.delete

使用 REST 测试您的更新

最后,您可以通过 Management API 对生产项目中的 Cloud Firestore 和 Cloud Storage 资源运行语法和语义测试。

使用 API 的此组件执行的测试包括:

  1. 定义TestSuite JSON 对象,以表示一组 TestCase 对象
  2. 提交 TestSuite
  3. 解析返回的 TestResult 对象

让我们在 testcase.json 文件中定义一个包含单个 TestCaseTestSuite 对象。在此示例中,我们传递与 REST 载荷和测试套件内嵌的规则语言源来运行这些规则。我们会指定规则评估期望,以及要测试规则集的客户端请求。您还可以指定测试报告的完整程度,方法是使用“FULL”值指示所有规则语言表达式(包括与请求不匹配的表达式)的结果都应包含在报告中。

 {
  "source":
  {
    "files":
    [
      {
        "name": "firestore.rules",
        "content": "service cloud.firestore {
          match /databases/{database}/documents {
            match /users/{userId}{
              allow read: if (request.auth.uid == userId);
            }
            function doc(subpath) {
              return get(/databases/$(database)/documents/$(subpath)).data;
            }
            function isAccountOwner(accountId) {
              return request.auth.uid == accountId
                  || doc(/users/$(request.auth.uid)).accountId == accountId;
            }
            match /licenses/{accountId} {
              allow read: if isAccountOwner(accountId);
            }
          }
        }"
      }
    ]
  },
  "testSuite":
  {
    "testCases":
    [
      {
        "expectation": "ALLOW",
        "request": {
           "auth": {"uid": "123"},
           "path": "/databases/(default)/documents/licenses/abcd",
           "method": "get"},
        "functionMocks": [
            {
            "function": "get",
            "args": [{"exact_value": "/databases/(default)/documents/users/123"}],
            "result": {"value": {"data": {"accountId": "abcd"}}}
            }
          ]
      }
    ]
  }
}

然后,我们可以使用 projects.test 方法提交此 TestSuite 以进行评估。

curl -X POST -d '{
    ' $(cat testcase.json) '
}' 'https://firebaserules.googleapis.com/v1/projects/secure_commerce/rulesets/uuid123:test'

返回的 TestReport(包含测试的 SUCCESS/FAILURE 状态、调试消息列表、访问过的规则表达式列表及其评估报告)将通过状态 SUCCESS 确认访问已正常获得允许。

管理跨服务 Cloud Storage 安全规则的权限

如果您创建的 Cloud Storage 安全规则使用 Cloud Firestore 文档内容来评估安全条件,则系统会通过 Firebase 控制台或 Firebase CLI 提示您启用相应权限以将这两款产品关联起来。

如果您决定停用此类跨服务安全性功能,请按以下步骤操作:

  1. 首先,在停用该功能之前,您需要先修改规则,移除使用规则函数控制对 Cloud Firestore 的访问权限的所有语句。否则,在停用该功能后,规则评估会导致 Storage 请求失败。

  2. 前往 Google Cloud 控制台中的 IAM 页面,参照撤消角色的 Cloud 指南删除“Firebase Rules Firestore Service Agent”角色。

当您下次从 Firebase CLI 或 Firebase 控制台保存跨服务规则时,系统会提示您重新启用该功能。