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

Firebase数据库规则配置:限制按邮箱匹配的读取权限

解决Firebase数据库权限问题:安全检查重复账号

你的问题核心在于客户端查询父节点/login时,父节点的读取权限未正确配置,同时直接在客户端做全局查询存在安全风险。我来帮你拆解问题并给出两种可行方案:

为什么你的原有规则无效?

Firebase的数据库规则是递归生效的:如果父节点/login的.read权限默认是false,哪怕子节点$uid的.read允许访问,客户端也无法在父节点上执行orderByChild这类需要遍历子节点的查询——因为查询操作需要先读取父节点下的所有数据进行过滤,而父节点权限直接阻止了这个行为。

你的规则1和2都只配置了子节点的读取权限,完全没处理父节点的查询场景,所以必然触发permission_denied。


方案一:用云函数处理重复检查(推荐,最安全)

直接让客户端查询整个/login节点存在数据泄露风险(哪怕规则限制,也可能被恶意利用枚举邮箱),更安全的做法是把重复检查逻辑放到云函数中,利用云函数的管理员权限完成查询,再返回结果给客户端。

步骤1:编写云函数

创建一个可调用云函数,负责检查邮箱/手机号是否已存在:

const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();

exports.checkDuplicateAccount = functions.https.onCall(async (data, context) => {
  // 验证用户是否已登录(如果是首次登录补充数据的场景)
  if (!context.auth) {
    throw new functions.https.HttpsError('unauthenticated', '请先登录');
  }

  const { targetEmail, targetPhone } = data;
  let query = admin.database().ref('login');
  let snapshot;

  // 根据传入的参数选择查询条件
  if (targetEmail) {
    snapshot = await query.orderByChild('email_principal').equalTo(targetEmail).once('value');
  } else if (targetPhone) {
    // 假设你存储了手机号字段`phone_principal`
    snapshot = await query.orderByChild('phone_principal').equalTo(targetPhone).once('value');
  } else {
    throw new functions.https.HttpsError('invalid-argument', '必须提供邮箱或手机号');
  }

  // 返回检查结果
  if (snapshot.exists()) {
    const duplicateUid = Object.keys(snapshot.val())[0];
    return { exists: true, uid: duplicateUid };
  } else {
    return { exists: false };
  }
});

步骤2:客户端调用云函数

替换原来的查询代码,改为调用云函数:

// 初始化云函数
const checkDuplicate = firebase.functions().httpsCallable('checkDuplicateAccount');

// 调用检查逻辑
checkDuplicate({ targetEmail: newEmail })
  .then(result => {
    const { exists, uid } = result.data;
    if (exists) {
      console.log(`该邮箱已绑定账号:${uid}`);
      // 处理重复账号逻辑,比如提示用户或合并账号
    } else {
      console.log('邮箱未被使用,可以继续操作');
      // 执行补充数据或账号创建逻辑
    }
  })
  .catch(error => {
    console.error('检查失败:', error.message);
  });

步骤3:收紧数据库规则

此时客户端不需要直接访问/login节点,所以可以把父节点权限锁死,只允许子节点的所有者和管理员访问:

{
  "rules": {
    "login": {
      ".read": false,
      ".write": false,
      "$uid": {
        ".read": "auth.uid === $uid || auth.token.admin === true",
        ".write": "auth.uid === $uid || auth.token.admin === true"
      }
    }
  }
}

方案二:调整数据库规则支持客户端查询(仅适合特定场景)

如果必须让客户端直接查询,你需要为父节点/login添加查询条件验证规则,确保用户只能查询特定的邮箱/手机号,而不能遍历整个节点:

{
  "rules": {
    "login": {
      // 允许管理员,或已登录用户查询自己的邮箱,或查询要补充的邮箱(结合手机号验证)
      ".read": "auth != null && (auth.token.admin === true || 
        (query.orderByChild === 'email_principal' && query.equalTo === auth.token.email) ||
        (query.orderByChild === 'email_principal' && exists(root.child('login').child(auth.uid).child('phone_principal'))))",
      "$uid": {
        ".read": "auth.uid === $uid || auth.token.admin === true",
        ".write": "auth.uid === $uid || auth.token.admin === true"
      }
    }
  }
}

规则说明:

  • 管理员拥有完全读取权限;
  • 已登录用户可以查询自己的邮箱(auth.token.email匹配email_principal);
  • 手机号登录的用户(已绑定手机号)可以查询要补充的邮箱(通过exists(root.child('login').child(auth.uid).child('phone_principal'))验证用户身份)。

⚠️ 注意:这种方式仍存在一定风险,比如恶意用户可能通过频繁调用查询枚举邮箱,所以还是推荐方案一。


内容的提问来源于stack exchange,提问作者Leandro Furini

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 09:22:34