Firebase安全规则:相似模拟写入为何结果不同?
嘿,这个问题其实戳中了Firebase安全规则最容易被忽略的核心逻辑——规则是盯着你写入的「目标路径」来匹配的,不是看你最终改了哪个节点的数据。这就是你俩操作结果不同的根本原因,咱一步步拆解:
先搞懂两个写入操作的本质区别
Write1:直接写
/users/id123/state
这个操作的目标就是state节点本身,所以Firebase会直接触发你在"state"节点下定义的.write规则。如果这条规则设了拒绝,那操作肯定直接失败,完全符合预期。Write2:写入
/users/id123/节点,数据里带state
这时候你的写入目标是用户的根节点/users/id123/,Firebase只会去匹配这个父节点的.write规则(如果有的话),根本不会碰state子节点的规则。哪怕你在提交的数据里包含了state的修改,只要你不是直接写state路径,Firebase就不会触发它的规则校验——相当于绕开了你给state设的权限限制。
这为啥会是个大问题?
比如你本来的想法是:state节点很重要,必须满足特定条件才能改,所以特意在state的.write里加了严格校验。结果别人只要通过写入父节点,就能随便改state里的数据,那你给state设的规则不就白忙活了?这肯定不是你想要的结果。
怎么 fix 这个漏洞?
要堵住这个口子,得从两方面下手:
- 管住父节点的写入权限:要么直接禁止直接写入
/users/id123/节点(比如设allow write: if false;),要么在父节点的.write规则里加校验,确保写入的数据里state字段的修改符合你设定的条件。 - 用
.validate规则补漏:.validate规则是检查最终数据状态的,不管你是直接写子节点还是通过父节点间接改,只要最终state的数据不符合要求,就会被拒绝。这相当于给state加了一道兜底的校验。
举个实际的规则例子参考:
rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /users/{userId} { // 禁止直接写入用户根节点,逼用户只能修改特定子节点 allow write: if false; match /state { // 这里放你原来的state写入权限条件 allow write: if request.auth.uid == userId; // 兜底校验state的数据格式,不管怎么改都得符合要求 validate: if request.resource.data.data is string && request.resource.data.data.size() <= 100; } } } }
如果确实需要允许写入父节点,那就在父节点的规则里校验state的修改:
match /users/{userId} { allow write: if // 要么没改state,要么改state的操作符合权限要求 (request.resource.data.state == resource.data.state) || (request.auth.uid == userId && request.resource.data.state.data is string); }
最后再划个重点
Firebase安全规则的核心逻辑一定要记牢:写入的目标路径决定了触发哪些规则。直接写子节点触发子节点规则,写父节点只触发父节点规则。要保证数据安全,不能只靠子节点的.write规则,必须配合父节点的权限控制和.validate规则,才能避免被绕开权限限制。
内容的提问来源于stack exchange,提问作者Luke Stanyer

