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

如何为Firebase Phone Auth实现移动端设备级速率限制?

Firebase Phone Auth 设备级速率限制实现方案

结合你已经完成的Play Integrity应用验证,以下是实现单设备每小时最多5条短信请求限制的具体步骤:

1. 选择可靠的设备标识

要实现设备级限制,首先得拿到稳定且不易伪造的设备标识:

  • 优先用Firebase Installations ID:通过FirebaseInstallations.getInstance().getId()获取,这是与应用绑定的唯一标识,卸载重装会重置,安全性更高,且无需额外权限。
  • 备选Android ID:通过Settings.Secure.ANDROID_ID获取,设备重置或刷机后会变更,但无需特殊权限,适合需要跨应用保持标识的场景。
  • 禁止使用IMEI、MAC地址:前者在Android 10+需要敏感权限,后者已被系统屏蔽获取,且易被伪造。

2. 后端存储速率限制数据

用Firebase的Firestore或实时数据库存储设备的请求计数和时间窗口信息,推荐Firestore:

  • 创建集合phoneAuthRateLimits,每个文档的ID使用哈希后的设备标识(比如SHA-256哈希,避免明文存储用户设备信息)。
  • 文档包含两个字段:
    • requestCount: 当前时间窗口内的请求次数
    • windowStart: 时间窗口的起始时间戳(单位毫秒)

3. 集成速率限制到短信请求流程

必须将限制逻辑放在后端(比如Firebase Cloud Functions),前端仅负责收集设备标识和调用后端接口,防止被绕过:

  1. 前端收集设备标识,同时获取Play Integrity验证令牌,一起发送到自定义云函数。
  2. 云函数先验证Play Integrity令牌(确保请求来自授权应用)。
  3. 云函数检查该设备的速率限制数据:
    • 如果设备无记录,或当前时间已超出窗口(距离windowStart超过1小时):重置计数为1,更新窗口起始时间为当前时间。
    • 如果在窗口内且请求次数≥5:返回错误,拒绝发起短信请求。
    • 如果在窗口内且次数<5:计数+1,更新数据库,允许发起短信请求。
  4. 前端收到允许结果后,再调用Firebase的signInWithPhoneNumber方法发送验证码。

4. 云函数示例(Node.js)

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

// 验证Play Integrity的方法(你已实现的逻辑)
async function verifyPlayIntegrity(token) {
  // 这里替换成你的Play Integrity验证逻辑
  return true;
}

exports.checkPhoneAuthRateLimit = functions.https.onCall(async (data, context) => {
  // 1. 验证应用合法性
  const isValid = await verifyPlayIntegrity(data.integrityToken);
  if (!isValid) {
    throw new functions.https.HttpsError("permission-denied", "请求来源未授权");
  }

  // 2. 处理设备标识(哈希后存储)
  const rawDeviceId = data.deviceId;
  const hashedDeviceId = crypto.createHash("sha256").update(rawDeviceId).digest("hex");
  
  const windowDuration = 60 * 60 * 1000; // 1小时毫秒数
  const maxRequests = 5;
  const rateLimitRef = admin.firestore().collection("phoneAuthRateLimits").doc(hashedDeviceId);
  const docSnapshot = await rateLimitRef.get();

  let requestCount = 1;
  let windowStart = Date.now();

  if (docSnapshot.exists) {
    const docData = docSnapshot.data();
    const currentTime = Date.now();
    
    // 检查是否在当前时间窗口内
    if (currentTime - docData.windowStart < windowDuration) {
      if (docData.requestCount >= maxRequests) {
        throw new functions.https.HttpsError("resource-exhausted", "请求过于频繁,请1小时后再尝试");
      }
      requestCount = docData.requestCount + 1;
      windowStart = docData.windowStart;
    }
    // 超出窗口则自动重置,requestCount=1,windowStart=当前时间
  }

  // 3. 更新速率限制数据
  await rateLimitRef.set({
    requestCount,
    windowStart
  });

  // 4. 返回允许请求的结果
  return { allowed: true, message: "可以发起短信验证请求" };
});

5. 额外优化建议

  • 定时清理过期数据:用Firebase定时云函数,删除windowStart超过24小时的文档,减少数据库存储占用。
  • 双重限制策略:可以同时限制同一手机号+设备的请求次数,进一步防止恶意请求。
  • 错误提示优化:给用户返回明确的冷却时间提示,比如“距离下次请求还有XX分钟”。

内容的提问来源于stack exchange,提问作者Pier1 Sys

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 10:52:22