先看现象。按邮箱做 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 必须在入口处归一化,用户可控的字符串直接当键等于把限流交给用户决定。这类问题最好一次改到位,反复小改反而容易把原因搅混。
