Firestore安全规则下用户间数字币转账的安全实现方案问询
安全实现Firestore数字币转账:只用安全规则就能搞定!
嘿,这个问题非常典型——很多开发者刚开始用Firestore做转账这类操作时都会踩这个坑:只验证登录状态,导致任何认证用户都能随意修改他人的余额。好消息是,完全可以通过Firestore安全规则来解决这个问题,不用额外的后端服务,就能把权限锁死。
先拆解问题核心
当前的规则只检查request.auth != null,相当于给所有认证用户开了“任意修改用户文档”的权限,恶意用户可以直接给自己的coins字段加钱,或者随意扣除别人的余额——这显然不行。我们需要的是:只有合法的转账操作(原子性的扣减发送者、增加接收者余额)才能修改他人的文档,其他任何操作都被禁止。
解决方案:结合事务+精准安全规则
Firestore的事务是原子性的,而安全规则可以检查事务的完整性,确保只有符合逻辑的转账操作才能通过。
第一步:正确的转账事务代码
首先,你的转账逻辑必须用Firestore事务实现(不能用批量写入,因为规则要依赖事务的上下文)。以JavaScript为例:
async function transferCoins(senderUid, receiverUid, amount) { const db = firebase.firestore(); try { await db.runTransaction(async (transaction) => { // 获取发送者和接收者的文档引用 const senderRef = db.collection('users').doc(senderUid); const receiverRef = db.collection('users').doc(receiverUid); // 读取当前余额(事务内的读取是锁住的,避免并发问题) const senderDoc = await transaction.get(senderRef); const receiverDoc = await transaction.get(receiverRef); // 基础校验:文档存在、余额足够 if (!senderDoc.exists || !receiverDoc.exists) { throw new Error('用户不存在'); } if (senderDoc.data().coins < amount) { throw new Error('余额不足'); } if (amount <= 0) { throw new Error('转账金额必须为正数'); } // 原子更新余额 transaction.update(senderRef, { coins: senderDoc.data().coins - amount }); transaction.update(receiverRef, { coins: receiverDoc.data().coins + amount }); }); console.log('转账成功!'); } catch (err) { console.error('转账失败:', err.message); } }
第二步:编写精准的安全规则
接下来是关键的安全规则,我们要限制:
- 用户只能读取自己的文档(保护隐私)
- 写入操作只有两种合法场景:
- 用户修改自己的文档(比如系统充值、提现,如果不需要可以去掉这个分支)
- 转账事务中,修改接收者的文档,同时发送者的余额被正确扣除对应金额
规则代码如下:
rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /users/{userId} { // 读取权限:只能读自己的文档 allow read: if request.auth != null && request.auth.uid == userId; // 写入权限:分两种合法场景 allow write: if // 场景1:用户修改自己的文档(比如系统给用户加钱,或者用户自己的其他合法操作) (request.auth.uid == userId) || // 场景2:合法的转账事务操作 (request.transaction && // 当前用户是发送者,正在修改接收者的文档 request.auth.uid != userId && // 预定义变量:发送者的原始文档、接收者的原始/新文档、转账金额 let senderOriginal = get(/databases/$(database)/documents/users/$(request.auth.uid)).data, let receiverOriginal = resource.data, let receiverNew = request.resource.data, let transferAmount = receiverNew.coins - receiverOriginal.coins, // 校验1:转账金额必须是正数(不能偷偷扣接收者的钱) transferAmount > 0 && // 校验2:发送者的余额足够,且事务后确实被扣了对应金额 senderOriginal.coins - transferAmount == getAfter(/databases/$(database)/documents/users/$(request.auth.uid)).data.coins); } } }
规则逻辑详解
- 读取规则:确保用户只能看到自己的余额,避免隐私泄露。
- 写入规则的场景2:
request.transaction:确保这是一个事务操作,避免单文档修改的恶意操作。transferAmount > 0:防止恶意用户通过“转账”的名义扣减接收者的余额。senderOriginal.coins - transferAmount == getAfter(...):验证发送者的余额在事务后确实减少了transferAmount,且原始余额足够(如果余额不足,这个等式不成立,规则会拒绝)。
额外的安全优化
如果你的应用不需要用户自己修改余额(所有余额变动都通过转账或系统操作),可以直接去掉场景1的分支,只保留事务的情况,这样安全性更高。
另外,还可以添加额外校验:
- 确保接收者的文档存在:
exists(/databases/$(database)/documents/users/$(userId)) - 限制转账金额的上限:
transferAmount <= 1000(根据你的业务需求调整)
总结
这个方案完全通过Firestore安全规则解决了你的安全隐患:恶意用户无法随意修改他人的余额,因为规则会严格校验事务的完整性,确保只有合法的转账操作才能通过。不需要额外的后端服务,就能实现安全的数字币转账功能。
内容的提问来源于stack exchange,提问作者Ajesh DS
相关产品推荐
相关产品推荐

