CDN 场景下登录限速的 key 选择陷阱

1 人参与

在 CDN 后面配置登录限速,最典型的故障往往源于限速 key 取错了值,而非 rate 设得太小:规则一上线,所有访客似乎都被算作同一个来源,正常用户连续刷新几次登录页就会收到 429。原因不复杂,limit_req_zone 默认按 $binary_remote_addr 计数,而在 CDN 或反向代理之后,Nginx 看到的这个地址是边缘节点的地址,不是真实客户端地址。一个边缘节点承载多少访客,这些访客就共享多少额度,等于对全站做了一次限速。

先恢复真实来源,再谈 key

修复的第一步不是改限速参数,而是在 http 块里配置 set_real_ip_from 与 real_ip_header,让 Nginx 从可信代理传递的头中还原客户端地址。这里有一个容易忽略的前提:信任范围必须只包含自己的 CDN 回源网段,不能图省事写成对所有来源信任。否则,客户端可以自行伪造转发头,限速 key 就失去了意义。配置完成后,先不要急着全量生效,取一条真实请求看日志里的 $remote_addr 是否已经变成客户端地址,确认还原链路正确,再让限速规则依赖它。

key 的选择比参数更敏感

真实 IP 回来之后,第二个陷阱才浮出水面:不是所有场景都适合继续用 IP 当 key。办公出口、公司 NAT、运维跳板常常让一批真实用户共用一个地址,按 IP 限速会把这些正常操作一并计入同一额度。素材里的做法值得借鉴:用 geo 标记可信来源,再用 map 把可信来源的 key 映射为空字符串。limit_req_zone 不会为空 key 建立状态,等于让这些请求直接不进入计数,这比先 allow 再 deny 更干净,也不会影响其他规则。

IPv6 是另一个隐蔽点。同一用户可以在一个前缀内频繁更换地址,如果日志中 IPv6 占比较高,单纯按完整地址计数会被稀释,攻击者换个后缀就绕过了。此时需要考虑按前缀聚合 key,而不是继续用完整地址。

验证要看被拦的是谁

规则上线后,最有效的检查不是看 429 总量,而是看被拦来源的分布。如果命中的 IP 集中在少数几个网段,并且有真实用户反馈,优先补白名单或放宽对应 zone;如果命中的地址极其分散、每个只出现一两次,才说明阈值整体偏紧。这个判断顺序能避免一看到 429 就整体取消规则。

CDN 场景下,限速 key 的选择本质上是在回答一个问题:这条规则到底想约束谁。边缘节点地址、共享出口、IPv6 前缀,每一个都可能让答案偏离预期。先把真实来源还原清楚,再决定哪些请求根本不参与计数,最后用日志验证拦截对象,限速才是一层可解释、可回滚的防护。

参与讨论

1 条评论
  • 剑锋无痕

    之前真被这个坑过,全站像共用一个额度