标准答案
- 按账号保护密码猜测,按 IP 和设备保护入口资源,按全局保护认证服务;计数应区分成功、失败和时间窗口。
- 失败后使用逐步退避、验证码或临时挑战,成功登录后按策略清理部分失败计数。
- 错误提示不能泄露账号是否存在,日志要记录风险信号但不记录密码;分布式计数要原子更新。
题目解析
登录限流要同时保护三种资源:账号维度防止密码猜测,IP/设备维度防止入口被打满,全局维度保护认证服务。单一维度要么容易绕过,要么容易被用来锁死真实用户。
失败计数、窗口、退避、验证码和临时挑战要根据风险逐步升级,成功后清理计数也要防止攻击者通过“成功”绕过其他维度。计数更新要原子且跨实例一致。
错误提示不能暴露账号是否存在,日志需要记录风险信号、规则命中和审计信息,但绝不能保存密码或可重放的完整 token。
代码示例
限流 Key 应组合多个维度而不是只用一个:
TypeScript
const keys = [
`login:account:${normalizedAccount}`,
`login:ip:${clientIp}`,
`login:device:${deviceFingerprint}`
]常见误区
- 误区:把密码或完整 token 放进限流 Key。改正:只使用规范化账号、IP、设备等非秘密标识,敏感值不能进入 Key 或日志。
- 误区:失败消息泄露账号存在。改正:对外使用一致错误提示,对内通过审计和风控信号定位攻击。
- 误区:只在单实例内计数。改正:使用共享原子计数或明确单实例范围,避免攻击者通过切换实例绕过。