Firebase实时数据库规则写入失败排查及安全优化咨询
Firebase Realtime Database 写入验证失败排查与安全方案优化
看来你在数据库规则验证和数据安全实现上碰到了棘手的问题——明明规则逻辑看起来没问题,但写入就是失败,只有放开权限才能正常工作。我先帮你拆解排查可能的原因,再给你一套更安全、更实用的实现思路。
一、为啥当前规则下写入失败?
你的规则核心是用Child1的计算值校验Child2,但大概率是浮点数精度差异在搞鬼:
- 客户端里
Child1是float类型,而Firebase Realtime Database存储数值时用的是双精度(double)。当你在客户端计算Child2时,float的精度限制可能会让结果和规则里用double计算的结果出现微小偏差(比如小数点后几位的差异),这就会导致规则里的等式不成立,写入被拦截。 - 另外,你得先确认客户端计算
Child2的逻辑和规则里的公式完全一致,有没有漏乘、符号写错这类低级错误?还有,写入的entryValues里是不是同时包含了Child1和Child2?如果其中一个字段缺失,规则里的val()会返回null,计算结果肯定对不上。
二、更安全的实现方案:把计算逻辑搬到服务端
你现在的方案是客户端算好Child2再提交,规则只是验证——这种方式其实很容易被破解,只要有人逆向你的客户端或者抓几次合法请求,就能模仿出符合规则的Child2值,根本绕不开验证。
真正安全的做法是让服务端(Firebase云函数)来负责计算和写入Child2,客户端只需要传Child1就行。这样客户端完全碰不到Child2的生成逻辑,从根源上杜绝篡改风险。
具体步骤:
- 收紧数据库规则:禁止客户端直接写入
Players节点,只允许云函数或者管理员账号操作。示例规则:
{ "rules": { ".read": "auth != null", "Players": { "$uid": { // 要么只允许当前用户通过云函数间接写(限制客户端不能传Child2) ".write": "auth != null && request.auth.uid === $uid && !newData.hasChild('Child2')", // 要么更严格:只允许Firebase服务账号(云函数用的账号)写入 // ".write": "serviceAccountAuth !== null" } } }
- 写一个Firebase云函数:处理客户端请求,计算
Child2并写入数据库。Node.js示例:
const functions = require("firebase-functions"); const admin = require("firebase-admin"); admin.initializeApp(); // 定义可调用云函数 exports.updatePlayerData = functions.https.onCall(async (data, context) => { // 首先验证用户是否登录 if (!context.auth) { throw new functions.https.HttpsError("unauthenticated", "请先登录"); } const uid = context.auth.uid; const child1 = data.child1; // 严格按照规则计算Child2,用double类型避免精度问题 const child2 = child1 * child1 * 1000000 + 1000 * child1 - 1000; // 如果Child2需要是整数,可以加Math.round()或者Math.floor()处理 // 写入数据库 try { await admin.database().ref(`/Players/${uid}`).update({ Child1: child1, Child2: child2 }); return { success: true, msg: "数据更新成功" }; } catch (err) { throw new functions.https.HttpsError("internal", "写入失败", err); } });
- 修改客户端代码:不再直接写数据库,转而调用云函数:
public async Task UpdatePlayerData(float child1) { if (AuthController.Instance == null || AuthController.Instance.user == null) { UnityEngine.Debug.Log("用户未登录,无法更新数据"); return; } // 获取云函数实例 var functions = FirebaseFunctions.DefaultInstance; var updateFunc = functions.GetHttpsCallable("updatePlayerData"); try { // 传参调用云函数 var result = await updateFunc.CallAsync(new Dictionary<string, object> { { "child1", child1 } }); UnityEngine.Debug.Log("数据更新完成"); } catch (System.Exception e) { UnityEngine.Debug.LogError($"更新失败:{e.Message}"); } }
这个方案的优势:
- 完全杜绝客户端篡改:计算逻辑在服务端,客户端根本不知道
Child2是怎么来的,就算逆向也没用。 - 解决精度问题:服务端用double计算,结果和数据库存储的类型一致,不会出现验证时的精度偏差。
- 权限更可控:客户端只能通过云函数间接操作数据库,避免了直接写入带来的各种风险。
三、临时修复方案(如果暂时不想用云函数)
要是你现在没时间接入云函数,可以先解决精度问题应急:
- 客户端计算
Child2时改用double类型,减少精度损失。 - 在规则里加个精度容错,允许微小的误差:
{ "rules": { ".read": "auth != null", ".write": "auth != null && Math.abs(newData.child('Child1').val() * newData.child('Child1').val() * 1000000 + 1000 * newData.child('Child1').val() - 1000 - newData.child('Child2').val()) < 0.0001" } }
但要注意,这只是临时方案,安全隐患依然存在,还是建议尽快迁移到云函数的方案。
内容的提问来源于stack exchange,提问作者Ztorum
相关产品推荐
相关产品推荐

