XFF 可信代理模型:反代/CDN 场景下 IP 不失真

CLARA轻量论坛系统
CLARA轻量论坛系统 星耀SVIP管理员 黑卡会员
发布于 2026-09-19 05:43 ·8 浏览 ·0 回复

X-Forwarded-For(简称 XFF,代理链路上逐跳追加的客户端 IP 列表)本身不可信,真正可信的是「你自己能控制的那一跳」;只要按可信代理白名单从右往左取第一个非可信 IP,反代和 CDN 场景下的 IP 就不会失真。

结论先行:XFF 是「追加」而不是「覆盖」

结论:XFF 的安全模型建立在「每一跳只追加、不删除」这个约定上,而客户端可以随便伪造初始值,所以最左边的地址永远最不可信。

一个典型的请求头可能是 `XFF: 1.2.3.4, 10.0.0.5, 172.16.0.9`,其中 `1.2.3.4` 是用户自己塞进去的假地址,后面两个才是真实链路。如果你直接取第一个值,等于把 IP 判定权交给了攻击者——IP 封禁、登录失败拦截、审计日志全部失效。

所以正确姿势只有一条:从右往左扫,跳过所有可信代理,取第一个不可信地址。

可信代理模型的算法长什么样

结论:标准算法是「从右向左找第一个不在白名单里的地址」,找不到就回退到 TCP 层的 `REMOTE_ADDR`。

伪代码:

ip = REMOTE_ADDR
if REMOTE_ADDR 不在可信代理集合: return REMOTE_ADDR
parts = XFF.split(",").map(trim)
for i = parts.length-1 down to 0:
    if parts[i] 不在可信代理集合: return parts[i]
return REMOTE_ADDR   // 全链可信,兜底

两个细节容易翻车:一是 XFF 里可能混入 `ip:port` 甚至 IPv6 带方括号,取到后要剥壳再校验;二是私有地址(10./172.16./192.168.)在做「同一 IP 高频失败拦截」时基本无意义,因为它们本身就是内网代理地址,需要区分对待。

Nginx 层最省事的做法

结论:在 Nginx 用 `real_ip` 模块改写 `REMOTE_ADDR`,后端代码一行都不用改。

set_real_ip_from 10.0.0.0/8;        # 自建反代
set_real_ip_from 173.245.48.0/20;   # CDN 回源段,按官方列表更新
real_ip_header    X-Forwarded-For;
real_ip_recursive on;               # 开启后会做「从右往左」扫描

`real_ip_recursive on` 就是上面那套算法的实现,搭配 `set_real_ip_from` 白名单才有意义;只开 `recursive` 不配白名单,等于信任所有来源。改完之后 PHP 里 `$_SERVER['REMOTE_ADDR']` 拿到的就是真实客户端 IP,`X-Real-IP`、`XFF` 保持原样供排查用。

反过来,如果后端应用自己解析 XFF,就必须保证 Nginx 传下去的 XFF 不被外部污染——建议在入口层直接用 `proxy_set_header X-Forwarded-For $remote_addr;` 覆盖,而不是 `$proxy_add_x_forwarded_for` 追加。

CDN 场景:优先用厂商专用头

结论:CDN 场景优先读 `CF-Connecting-IP`、`True-Client-IP`、`Ali-CDN-Real-IP` 这类专用头,比解析 XFF 更可靠。

这些头由 CDN 边缘节点直接写入,不参与追加链条,伪造难度高得多。对应 Nginx 配置:`real_ip_header CF-Connecting-IP;`。同时要正视一个前提——源站必须只允许 CDN 回源段访问(防火墙白名单、安全组,或启用 CDN 的回源鉴权)。否则攻击者直接解析源站 IP 打过来,伪造什么头都拦不住,因为请求根本不经过 CDN。

另外 CDN 的 IP 段会变,回源白名单建议做成计划任务定期同步,别写死一次管三年。

别忘了兜底校验

结论:可信代理模型解决的是「取哪个值」,取到之后还得校验格式与合理性。

  • 用 `filter_var($ip, FILTER_VALIDATE_IP)` 卡掉非法串;
  • 对 XFF 长度设上限(比如 20 段),超长直接截断或降级为 `REMOTE_ADDR`,防止超长头部打日志;
  • 内网封禁规则里排除私有网段,避免把自家反代封了;
  • 日志同时记录 `REMOTE_ADDR` 和原始 XFF,出现争议时可回溯。

这套东西对依赖 IP 的功能是刚需:IP 封禁、同 IP 登录失败拦截、登录审计日志,判错一个字段,防密码喷洒就等于没做。

收束

可信代理模型的核心只有三句话:白名单里只写你自己控制的代理、从右往左取第一个非可信 IP、取完再校验格式和私有网段。Nginx 的 `set_real_ip_from` + `real_ip_recursive` 是最省事的落地方案,CDN 场景换成厂商专用头并锁死源站回源白名单。头是不可信的,拓扑才是可信的。

本文转载自 Clara轻量论坛系统,原文地址:https://www.leleweb.cn/thread-504.html
转载请注明出处,版权归原作者所有。

全部回复 0

还没有回复,来抢沙发~