如何为Firebase邮箱密码认证设置登录尝试速率限制?
Firebase邮箱密码认证:限制登录尝试次数防范暴力攻击
Firebase Auth本身没有提供可直接配置的登录失败次数限制与延迟功能,但可以通过Cloud Functions + Firestore自定义实现符合你需求的速率限制逻辑,具体方案如下:
核心思路
通过Firestore记录每个用户的登录失败次数与锁定截止时间,在登录请求执行前检查锁定状态,登录失败时更新计数并触发锁定(支持指数增长的超时时间),登录成功时重置计数。
实现步骤
1. 存储登录尝试记录
在Firestore中创建loginAttempts集合,每个文档以用户邮箱(或哈希后的邮箱)为ID,包含以下字段:
failedAttempts: 连续登录失败次数(初始为0)lockUntil: 锁定截止时间戳(初始为0,即无锁定)
2. 封装带速率限制的登录云函数
使用Cloud Functions的Callable Function封装登录逻辑,确保所有登录请求都经过验证:
const functions = require("firebase-functions"); const admin = require("firebase-admin"); admin.initializeApp(); exports.loginWithRateLimit = functions.https.onCall(async (data, context) => { const { email, password } = data; const loginAttemptRef = admin.firestore().collection("loginAttempts").doc(email); const doc = await loginAttemptRef.get(); // 检查是否处于锁定状态 if (doc.exists) { const { lockUntil } = doc.data(); if (Date.now() < lockUntil) { const remainingMinutes = Math.ceil((lockUntil - Date.now()) / 60000); throw new functions.https.HttpsError( "permission-denied", `登录失败次数过多,请${remainingMinutes}分钟后再试` ); } } try { // 执行登录验证 const userCredential = await admin.auth().signInWithEmailAndPassword(email, password); // 登录成功,重置失败记录 await loginAttemptRef.set({ failedAttempts: 0, lockUntil: 0 }); return { success: true, uid: userCredential.user.uid }; } catch (error) { // 登录失败,更新失败次数与锁定状态 let failedAttempts = 1; let lockUntil = 0; const baseLockTime = 10 * 60 * 1000; // 基础锁定时长:10分钟 if (doc.exists) { failedAttempts = doc.data().failedAttempts + 1; // 每5次失败,锁定时间指数增长(10分钟 → 20分钟 → 40分钟...) if (failedAttempts % 5 === 0) { const multiplier = Math.pow(2, Math.floor(failedAttempts / 5) - 1); lockUntil = Date.now() + baseLockTime * multiplier; } } await loginAttemptRef.set({ failedAttempts, lockUntil }); throw new functions.https.HttpsError("invalid-argument", error.message); } });
3. 客户端调用云函数登录
在客户端替换原生的signInWithEmailAndPassword,改为调用上述云函数:
const loginWithRateLimit = firebase.functions().httpsCallable('loginWithRateLimit'); async function submitLogin(email, password) { try { const result = await loginWithRateLimit({ email, password }); // 处理登录成功逻辑(如跳转主页) console.log('登录成功,用户UID:', result.data.uid); } catch (error) { // 提示用户错误信息 alert(error.message); } }
4. 配置Firestore安全规则
防止客户端篡改登录尝试记录,仅允许云函数访问loginAttempts集合:
rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /loginAttempts/{email} { allow read, write: if request.auth.token.firebase.sign_in_provider == "service_account"; } } }
额外优化建议
- 邮箱哈希处理:将邮箱进行SHA-256哈希后作为文档ID,避免直接暴露用户邮箱。
- 定期清理数据:使用Cloud Functions定时任务,删除
lockUntil小于当前时间的过期记录,节省存储空间。 - 锁定提示优化:在客户端显示剩余锁定时间,提升用户体验。
内容的提问来源于stack exchange,提问作者Rusty Miller
相关产品推荐
相关产品推荐

