Repository navigation
[Bug]: 认证限流共享 IP 计数桶导致正常登录被误拦截 #292
Copy link
Copy link
Description
现象
用户在正常登录过程中,连续登录 2–3 次后可能收到「请求过于频繁,请稍后再试」或「请求过于频繁」,即使邮箱和密码均正确。
该现象不是登录面板重复提交造成的。前端提交期间会锁定按钮,已有测试也断言一次表单提交只调用一次登录 API。问题来自后端限流计数桶的粒度与执行顺序。
已验证复现
验证基线:upstream/main@270227135d9b1ebdc9864ac0946ee8fc64087690。使用独立 worktree、独立 Redis 和本地测试账号,不复用生产数据。
场景一:干净窗口
同一 IP 连续调用 POST /auth/login:
- 第 1–10 次:登录成功,业务码
200 - 第 11 次:业务码
429,提示「请求过于频繁,请稍后再试」
这符合当前「敏感接口单 IP 10 次/分钟」的实现。
场景二:同一 IP 已有其他认证请求
先在同一计数窗口内发出 8 次其他敏感认证请求,再使用正确密码登录:
- 第 1 次登录:成功
- 第 2 次登录:成功
- 第 3 次登录:业务码
429,提示「请求过于频繁,请稍后再试」 - Redis 中敏感接口计数为
11
参数校验失败的认证请求同样会占用额度,因为限流发生在路由参数校验和业务处理之前。
场景三:同一 IP 已有普通 API 流量
先在同一计数窗口内发出 58 次普通 API 请求,再使用正确密码登录:
- 第 1 次登录:成功
- 第 2 次登录:成功
- 第 3 次登录:业务码
429,提示「请求过于频繁」 - Redis 中全局计数为
61
因此,「登录 2–3 次就触发限流」可以由当前主仓代码稳定复现;具体线上一次命中的是敏感接口桶还是全局桶,需要结合生产 windup.ratelimit 日志或 Redis 计数确认,但不影响缺陷本身成立。
根因
RateLimitMiddleware 当前同时存在三层问题:
- 不同认证动作共用同一个 IP 桶。
/auth/register、/auth/login、/auth/send-code、/auth/login-by-code、/auth/reset-password全部写入ratelimit:sensitive:{ip},合计只有 10 次/分钟。 - 普通 API 与登录共享全局 IP 桶。 所有请求都会先写入
ratelimit:api:{ip},上限 60 次/分钟。页面加载产生的业务请求会挤占后续登录额度。 - 用户级限流实际上拿不到用户身份。 请求顺序是
RateLimitMiddleware → AuthMiddleware → route,但用户级检查在call_next之前读取request.state.current_user。此时鉴权尚未执行,user_id为空,所以声明的 120 次/分钟用户桶没有真正覆盖已登录请求。
此外,密码登录服务本身已经具备按邮箱记录错误密码的保护:连续 5 次失败后锁定 15 分钟;发验证码也已经有按邮箱 60 秒冷却。当前 IP 共享桶叠加在这些细粒度保护之外,主要增加了正常用户被误伤的概率。
影响
- 同一用户在登录、注册、发码等操作之间切换时,会互相消耗额度。
- 办公网、校园网、家庭 NAT 等共享出口 IP 下,不同用户会互相消耗额度。
- 已有页面流量可能导致后续正确登录被拒绝。
- 若生产代理链未正确还原客户端 IP,误伤范围会扩大到代理后的全部用户。
- 当前实现与认证模块原本描述的「账号级限流防暴力破解」不一致。
期望行为
- 正确密码登录不应因为同一 IP 上此前的普通 API、发码、注册或其他账号操作,在第 2–3 次就被拒绝。
- 密码暴力破解、验证码滥发和匿名流量攻击仍应分别受到限制,不能通过删除限流或单纯大幅调高阈值解决。
- 共享出口 IP 下,不同账号的正常认证操作不应直接共用一个过小的账号保护额度。
- 已登录请求应真正进入用户级限流;匿名请求保留合理的 IP 级兜底。
建议修改边界
- 将密码登录、发码、注册、验证码登录、重置密码拆分为独立限流策略,不再共用一个 10 次/分钟的敏感接口桶。
- 密码登录以失败尝试为主要保护信号,至少同时具备账号维度与 IP 维度的防护;成功登录不消耗账号的错误密码额度,并保留更高的粗粒度防滥用上限。
- 保留现有按邮箱的错误密码锁定和发码冷却,避免出现两套含义重叠但阈值不一致的计数。
- 调整限流与鉴权的数据流,使已登录请求能按
user_id计数;匿名请求再退回 IP 维度。 - 为各限流分支记录可区分的日志信息,但不要记录邮箱、token 等敏感值。
本 Issue 不包含登录页 UI 改版、认证提示文案重写、JWT 时效调整,也不改变现有业务响应 envelope。
验收标准
- 同一 IP 在一分钟内已有至少 8 次其他认证端点请求后,连续 3 次正确密码登录仍可成功,不会命中共享敏感接口桶。
- 同一 IP 在一分钟内已有至少 58 次普通 API 请求后,连续 3 次正确密码登录仍不会被全局匿名桶误拦截。
- 两个不同邮箱位于同一 IP 时,不共享账号级错误密码计数;同时保留可验证的 IP 级攻击上限。
- 错误密码达到既定阈值后仍会触发保护,正确密码成功后错误计数按现有契约清理。
-
/auth/send-code的单邮箱 60 秒冷却仍然生效,且不会消耗密码登录额度。 - 自动化测试证明已登录请求能够实际进入用户级限流分支,而不是因中间件顺序始终跳过。
- 自动化测试覆盖直连、可信代理
X-Forwarded-For与共享出口 IP 场景。 - 限流日志可以区分全局匿名、认证端点、账号失败和已登录用户四类触发来源,且不泄漏敏感信息。
关联
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
bugSomething isn't workingSomething isn't working
Activity
xiaocheny214 commentedon Aug 13, 2026
明白,我会跟进这个issue。
fix(ratelimit): 重构为两阶段限流,修复认证桶共享问题
xiaocheny214 commentedon Aug 14, 2026
当前限流逻辑为细粒度的业务级别限流,容易误伤用户且影响到核心业务功能的开发。现准备移除相关限流逻辑,改为粗粒度的nginx网关限流。
fix(auth): 移除共享 IP 限流中间件,避免正常登录被误拦截 (1024XEngineer#325)