You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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);
  }
}

第二步:编写精准的安全规则

接下来是关键的安全规则,我们要限制:

  1. 用户只能读取自己的文档(保护隐私)
  2. 写入操作只有两种合法场景:
    • 用户修改自己的文档(比如系统充值、提现,如果不需要可以去掉这个分支)
    • 转账事务中,修改接收者的文档,同时发送者的余额被正确扣除对应金额

规则代码如下:

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 21:02:36