Firebase Realtime Database和Cloud Firestore的security rules是一层部署在谷歌服务器上的访问控制逻辑,它独立于客户端运行,任何来自移动端或Web端的读写请求都必须先通过规则校验。很多团队在原型阶段为了省事把规则写成只读或只写全开,上线后忘记收敛,结果数据库被搜索引擎收录或遭脚本批量拖库。理解规则的匹配机制和表达式语法,是构建可信后端的第一步。

规则匹配模型与基础语法结构
Firebase security rules采用层级路径匹配的方式,每一个.write或.read节点都对应数据库中的某一段路径。当客户端尝试访问/users/uid/profile时,系统会从根节点向下逐级查找最具体的匹配规则,并依次评估布尔表达式。如果任一父级节点拒绝了访问,子节点即便允许也无法覆盖。这种自顶向下的继承模型要求开发者在写通配符时必须想清楚每一层的暴露面。
在Realtime Database中,规则文件是一个JSON对象,而Firestore使用的是类JavaScript的语法。两者核心都围绕auth、data、newData这几个预定义变量。例如auth.uid代表当前已登录用户的唯一标识,data是写操作前已存在的快照,newData是即将写入的内容。利用这些变量可以写出诸如“只允许本人修改自己的资料”这样的约束,而不依赖任何应用层校验。
下面是一段典型的Realtime Database规则,展示如何用通配符捕获用户ID并比对身份。注意$uid是路径参数,在表达式里可直接引用。这种写法比给整个users节点开放写权限安全得多,因为越权用户拿不到别人的路径参数匹配。
{
"rules": {
"users": {
"$uid": {
".read": "auth != null && auth.uid == $uid",
".write": "auth != null && auth.uid == $uid"
}
}
}
}
用auth与exists函数阻断越权访问
仅仅判断auth != null是不够的,匿名登录用户同样拥有合法auth对象,却不应该看到其他人的私信。更精细的控制需要结合exists()和get()函数,在规则里跨节点查询关联数据。比如在“好友关系”节点中,可以检查发起请求的用户是否存在于对方的好友列表,再决定是否放行读取私聊消息。
Firestore的规则语言支持get()异步获取其他文档,这在Realtime Database里对应root.child().exists()。实践里常见错误是把校验逻辑全部推到客户端,认为“前端不显示就等于安全”,但攻击者直接用REST API绕过前端就能读到原始数据。把关系型判断下沉到规则层,才能确保即使客户端被破解,数据边界依然牢固。
以下示例展示在Firestore中限制私聊频道:只有参与双方可以读,且要求其中一方在成员数组中。这里使用request.auth与resource.data做双向比对,避免用客户端传来的字段作为信任依据。
match /chats/{chatId} {
allow read: if request.auth != null
&& (request.auth.uid == resource.data.memberA
|| request.auth.uid == resource.data.memberB);
allow write: if request.auth != null
&& (request.auth.uid == request.resource.data.memberA
|| request.auth.uid == request.resource.data.memberB);
}
避免通配符滥用与公开节点的收敛策略
通配符$让规则可以适配动态路径,但也容易写成".write": true挂在通配符下,导致整棵子树裸奔。正确的做法是对不同业务子树分别声明最小权限,把公开只读的内容和需要身份的内容拆开。例如博客文章的元数据可以公开读,但草稿状态字段必须限制作者本人。
另一个被忽视的点是索引与规则无关:即使规则拒绝了列表查询,攻击者仍可能通过猜测ID逐一探测。因此对于敏感集合,应附加validate规则限制数据类型和长度,防止脏数据撑爆配额或被用作注入跳板。配合Firebase的模拟器套件在本地跑测试用例,能把规则错误消灭在发布前。
下面给出一段带validate的Realtime Database示例,限定用户名只能是字符串且长度小于二十,同时禁止匿名用户创建任何帖子。这种写法把结构校验也收口到服务器端,降低后端清洗成本。
{
"rules": {
"posts": {
"$postId": {
".read": true,
".write": "auth != null && newData.hasChild('author')",
"title": {
".validate": "newData.isString() && newData.val().length < 20"
}
}
}
}
}
把security rules当作代码而非配置文件来对待,接入版本管理和CI检查,是团队规模放大后的必然选择。每次改动都通过firebase emulators:exec跑一遍断言脚本,能够防止“本地能写线上拒绝”或反之的尴尬。安全边界清晰了,产品迭代速度反而更快。
Firebasesecurity_rulesdatabase_protection修改时间:2026-08-18 09:50:27