2.3 安全规则心得:默认拒绝、调试与常见误配

安全规则是四大件里最容易被轻视、出事后最致命的一件。这一节讲清楚它的定位、正确的起步姿势,以及那些我见过最多的误配。

规则不是可选项,是真正的防线

先建立一个关键认知:客户端 SDK 是直连数据库的。 你的 App 用户手里拿着的是能直达 Firestore 的连接,中间没有你自己写的服务端把关。服务端项目里,接口层天然是一道闸门;而 Firebase 项目里,唯一站在数据库门前的,就是安全规则。

所以规则不是合规装饰,不是“以后有空再补”的东西。规则没配好,等于把数据库的管理工具连同地址一起发给了用户。正确的心法是四个字:默认拒绝——所有路径默认谁都不许动,然后一条条放开,每放开一条都能说清理由。

// 起步姿势:一切拒绝(示意,以官方文档为准)
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} { allow read, write: if false; }
  }
}

规则语言的三块积木

规则表达式主要围绕三个变量展开,理解它们就理解了大半:

积木 它是什么 典型用法
auth 当前登录用户的信息 校验请求者 UID 与文档归属字段一致
request 这次请求本身 用 request.resource.data 校验写入数据的类型与范围
resource 被操作的现有文档 比较新旧值,比如价格字段不许客户端改

比如“只能改自己的文档”,就是 auth 的用户 ID 等于 resource 里的 owner 字段;“价格字段不许客户端改”,就是比较 request 和 resource 里的价格。数据校验(字段类型、取值范围)也写在这里,规则就是你的数据守门员。

常见误配:三种典型姿势

误配往往不是没配,而是“以为配了”。三种最典型的:

常见误配 风险 正确姿势
把 auth != null 当授权 任何注册用户都能读写全库,等于裸奔 逐集合限定:进一步校验 UID 与文档归属字段一致
时间相关校验放客户端 客户端时间可随意改,限制形同虚设 校验逻辑放到规则里用服务器时间,或交给云函数
误解规则不递归 以为配了上层就覆盖了子路径,下层实际裸露 通配符用 recursive 方式显式覆盖全路径,逐层核对

第一种最常见。开发图快,写一句“已登录就放行”,项目一路跑到上线。任何能让用户注册的产品,这句规则都约等于不设防。

调试:模拟器是你的好朋友

规则改错了不能拿生产库试。我的工作流是模拟器先行:规则编辑器里的模拟器可以指定操作类型、伪造身份、填入文档数据,跑一遍立刻告诉你允许还是拒绝、卡在哪条规则上。每写完一条新规则,先在模拟器里把“该过的场景”和“不该过的场景”各跑一遍,都符合预期再部署。

模拟器套件(Emulator Suite)更进一步,可以在本地把整个 Firestore 和规则跑起来,配合测试框架做批量断言,团队协作时尤其值得搭一套。规则 Playground 思路也一样:把规则当成有测试用例的代码,而不是配置文件。我的模拟器检查清单:

  • 每条新规则先跑“该放行”的场景,确认不误伤;
  • 再跑“该拒绝”的场景,确认没有后门;
  • 身份分匿名、普通用户、他人三种各过一遍;
  • 全部通过才部署,部署记录留档。

一条实用的协作习惯

规则文件跟代码一样进版本管理,每次改动走评审。规则的一行差别就是全库敞开和铁桶的差别,它值得被认真 review。部署节奏上,我习惯规则变更和数据结构变更一起提交,避免出现“字段改了规则没跟上”的真空期。

本节小结

规则是 Firebase 项目唯一的安全防线:默认拒绝起步,用 auth、request、resource 三块积木逐集合放权;别把已登录当已授权,时间校验别信客户端;改动先过模拟器,规则文件进版本管理。做到这几条,绝大多数安全事故与你无缘。