先看现象。按邮箱做 10 次每分钟的限流,前端传 A@x.com 和 a@x.com 加尾随空格被当成两个 key,同一账号轻松绕过限制。

真正麻烦的地方在于,几个原因的现象几乎一样。没有做 trim 和大小写归一。还有一种是直接拿原始输入当 Redis key。

还有一种情况,URL 编码差异导致同值不同形。

另外,多端登录时大小写不一致。

第一件事是对标识做 trim 和转小写

python -c "print('key = email.strip().lower()')"

优先用不可变的 user_id 替代邮箱

python -c "print('key = f\'rl:user:{user_id}\'')"

然后在网关层统一加作用域前缀

python -c "print('rl:chat:{user_id}')"

等前面都确认过,再用变形输入验证限流是否真的生效

python hammer.py --emails 'A@x.com,a@x.com ,a@x.com' --n 30

这台环境是别人交接的,不少默认值被动过,只能一项项核。

同事提醒我看一下最近一次发布,时间点确实对得上。

恢复的判定其实很直接。三种写法的邮箱共用同一个计数,第 11 次请求返回 429,绕过测试全部失败。

限流 key 必须在入口处归一化,用户可控的字符串直接当键等于把限流交给用户决定。这类问题最好一次改到位,反复小改反而容易把原因搅混。

作者 9823189