跳转到帖子

授权渗透中如何验证 Nginx 客户端 IP 信任边界是否可绕过后台限制

精选回复

发布于

问题背景

在一次授权渗透测试中,目标后台通过来源 IP 限制访问:外部请求返回 403,办公网或 VPN 地址可以正常访问。入口链路为 CDN、负载均衡和 Nginx,应用日志却偶尔记录到不属于实际出口的内网 IP。

这类现象通常不是防火墙失效,而是代理层错误信任了客户端可控的 X-Forwarded-For,导致 Nginx 或后端应用将伪造地址当成真实客户端 IP。验证重点应放在信任链和变量变化上,而不是继续操作后台功能。

以下步骤仅适用于明确授权的目标。确认伪造请求能够使响应由 403 变为 200 或进入认证页后,应立即停止,不读取业务数据、不执行状态变更操作。

问题定位

先确认限制发生在哪一层,常见位置包括:

  • Nginx 使用 allow、deny 或基于 $remote_addr 的条件判断。
  • 应用读取框架解析后的客户端 IP,并据此决定是否开放管理路由。
  • 负载均衡、安全网关或 WAF 根据源地址执行访问策略。
  • 应用同时信任 X-Forwarded-For、X-Real-IP 等多个请求头,产生解析差异。

建议先建立最小证据链:记录未携带伪造头时的状态码、携带测试地址时的状态码、Nginx 看到的 TCP 对端地址、解析后的客户端地址以及原始转发头。不要仅凭页面内容判断。

技术分析

Nginx 加载 ngx_http_realip_module 后,可以使用代理头重写 $remote_addr。危险配置通常类似:

set_real_ip_from 0.0.0.0/0;
real_ip_header X-Forwarded-For;
real_ip_recursive on;

location /admin/ {
    allow 10.0.0.0/8;
    deny all;
    proxy_pass 
}

set_real_ip_from 0.0.0.0/0 表示任意来源都被视为可信代理。攻击者可以直接发送 X-Forwarded-For: 10.20.30.40,使访问控制看到一个伪造的内网地址。

在启用 Real IP 模块后,几个变量的含义需要区分:

  • $remote_addr:经过 Real IP 模块处理后的客户端地址。
  • $realip_remote_addr:建立 TCP 连接的原始对端地址,可用于保留入口证据。
  • $http_x_forwarded_for:请求中收到的原始 X-Forwarded-For 内容,可能完全由客户端控制。

当 real_ip_recursive on 开启时,Nginx 会沿地址链从右向左排除可信代理,选择最后一个非可信地址。如果可信代理网段配置过宽,整个地址链都可能由请求方影响。

授权验证步骤

第一步,使用不存在敏感数据、不会触发状态变化的后台入口作为测试路径,例如后台登录页或专门准备的健康检查路径。先发送基线请求:

curl -sk -o /dev/null -D - \
   \
  -H 'User-Agent: authorized-ip-boundary-test/1.0'

第二步,在授权范围内添加一个文档化的测试地址。建议使用组织分配的测试网段;若只是验证解析行为,也可以使用 RFC 5737 文档地址,避免冒充真实员工地址:

curl -sk -o /dev/null -D - \
   \
  -H 'X-Forwarded-For: 192.0.2.123' \
  -H 'User-Agent: authorized-ip-boundary-test/1.0'

如果必须验证“内网白名单是否可被绕过”,应由系统负责人提供专用测试地址,并在变更窗口内完成。不要自行枚举内网段。判断依据应是状态码或固定响应头发生变化,而不是继续浏览后台页面。

第三步,测试常见解析差异。每次只改变一个变量,并保存完整响应头:

# 单一地址
curl -sk -o /dev/null -D -  \
  -H 'X-Forwarded-For: 192.0.2.123'

# 模拟经过一层已知代理后的地址链
curl -sk -o /dev/null -D -  \
  -H 'X-Forwarded-For: 192.0.2.123, 198.51.100.10'

# 检查应用是否错误信任另一个头
curl -sk -o /dev/null -D -  \
  -H 'X-Real-IP: 192.0.2.123'

若直接暴露的入口能够通过任意客户端头改变访问结果,可初步判定存在信任边界问题。若只有经过指定负载均衡时才生效,则需要继续检查负载均衡是否覆盖客户端提交的头,还是仅在其后追加地址。

服务端排查方法

在具备服务器审计权限时,先查看最终生效的 Nginx 配置,而不是只检查某个配置文件:

sudo nginx -T 2>&1 | grep -nE \
'set_real_ip_from|real_ip_header|real_ip_recursive|allow |deny |proxy_set_header'

sudo nginx -V 2>&1 | grep -o -- '--with-http_realip_module'

重点检查是否存在以下情况:

  • set_real_ip_from 0.0.0.0/0 或覆盖范围明显过大的网段。
  • 同时信任 CDN、负载均衡和任意内网地址,但无法确认每一跳是否会清洗请求头。
  • Nginx 已重写 $remote_addr,后端又对 X-Forwarded-For 进行第二次解析。
  • 公网可以绕过 CDN 或负载均衡,直接访问源站。
  • IPv4 配置正确,但遗漏 IPv6 入口或 IPv4 映射地址。

为便于复现,可以临时增加独立日志格式,同时保留原始连接地址和解析结果:

log_format ip_boundary '$time_iso8601 request_id=$request_id '
  'peer=$realip_remote_addr parsed=$remote_addr '
  'xff="$http_x_forwarded_for" xri="$http_x_real_ip" '
  'host="$host" request="$request" status=$status';

access_log /var/log/nginx/ip-boundary.log ip_boundary;

然后按测试标识或时间窗口检索:

sudo grep 'authorized-ip-boundary-test' /var/log/nginx/access.log
sudo tail -n 100 /var/log/nginx/ip-boundary.log
sudo journalctl -u nginx --since '15 minutes ago'

如果日志中 peer 是测试人员的公网出口,而 parsed 变成请求头中提交的地址,即可确认 Real IP 信任范围存在问题。如果 peer 是负载均衡地址,则还需结合负载均衡访问日志确认它是否覆盖了客户端原有的 X-Forwarded-For。

推荐修复配置

只信任实际会连接到 Nginx 的代理地址,不要直接信任整个内网,更不能信任全部来源。示例中的网段必须替换为真实、受控的负载均衡出口:

# 仅信任实际负载均衡节点
set_real_ip_from 10.40.8.0/24;
set_real_ip_from 2001:db8:40:8::/64;
real_ip_header X-Forwarded-For;
real_ip_recursive on;

location /admin/ {
    allow 10.60.0.0/16;
    deny all;
    proxy_pass 
}

同时在最外层代理覆盖客户端提交的同名头,而不是直接透传。对于由 Nginx 承担的最外层反向代理,可采用:

proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Real-IP $remote_addr;

如果代理链确实需要保留历史地址,应确保只有受信任的入口负责生成地址链,并在网络层阻止客户端直连源站。是否使用 $proxy_add_x_forwarded_for 要结合入口位置判断:它会保留请求中已有的 X-Forwarded-For,因此不适合作为未受信任公网入口的清洗手段。

应用层补充检查

即使 Nginx 配置正确,框架自身的代理信任配置也可能重新引入问题。检查应用是否无条件启用了“信任所有代理”,并确认它读取的地址与 Nginx 日志一致。

  • 应用只应信任固定代理数量或明确代理网段。
  • 不要用用户可控请求头直接进行管理员授权。
  • IP 白名单只能作为附加控制,后台仍应启用独立认证和多因素认证。
  • 对来源地址解析结果设置统一字段,避免网关、应用和审计系统各自解析。

风险定级与证据保留

若伪造地址只能改变日志显示,但不能影响任何安全决策,风险通常偏低,但会削弱溯源能力。若可绕过后台入口限制、接口限流或高权限操作保护,则应按访问控制绕过评估。若后台本身没有二次认证,风险需要进一步上调。

报告中建议保留以下证据:

  • 测试时间、授权范围和测试出口 IP。
  • 基线请求与带测试头请求的状态码差异。
  • Nginx 中 peer、parsed 和原始转发头的对应关系。
  • 最终生效配置中具体的信任网段。
  • 修复后相同请求重新返回 403 的复测结果。

总结

这类问题的核心不是 X-Forwarded-For 本身,而是系统是否能够明确区分“谁有权声明客户端地址”。授权验证时应以最小影响方式确认响应差异,再通过 Nginx 生效配置和同时记录原始、解析后地址的日志完成定因。修复原则是缩小可信代理范围、由最外层入口清洗头部、阻断源站直连,并避免将 IP 白名单作为唯一的后台授权机制。

网络流量与边界分析示意图
网络请求、日志与边界流量分析示意
服务器与云环境安全示意图
服务器、云资产与权限边界梳理示意

这类验证建议先把链路画清楚:客户端 → CDN/WAF/LB → Nginx → 应用。重点不是单纯伪造某个 Header,而是确认“哪个节点有权声明客户端 IP”,以及后台 ACL 最终使用的是哪个变量。

先临时增加审计日志,同时记录连接源地址、重写后的地址和常见转发头:

log_format ip_boundary
    '$time_iso8601 host=$host uri="$request_uri" status=$status '
    'remote=$remote_addr original=$realip_remote_addr '
    'xff="$http_x_forwarded_for" xri="$http_x_real_ip" '
    'forwarded="$http_forwarded" cfip="$http_cf_connecting_ip"';

access_log /var/log/nginx/ip-boundary.log ip_boundary;

然后用 nginx -T 查看实际生效配置,不要只看某个配置文件:

nginx -T 2>&1 | grep -nE \
'set_real_ip_from|real_ip_header|real_ip_recursive|proxy_protocol|allow |deny |satisfy|auth_request|location'

从非授权网段发起基线请求,再分别注入常见客户端 IP 头。正常情况下,来自非可信连接源的这些头不应改变 $remote_addr,后台仍应拒绝:

curl -k -i 

curl -k -i  \
  -H 'X-Forwarded-For: 127.0.0.1'

curl -k -i  \
  -H 'X-Forwarded-For: 10.10.10.10'

curl -k -i  \
  -H 'X-Real-IP: 10.10.10.10'

curl -k -i  \
  -H 'Forwarded: for=10.10.10.10'

需要重点关注以下几类配置问题:

  • set_real_ip_from 0.0.0.0/0、过大的内网段或整个容器网段被设为可信代理。
  • 源站可以从公网直连,但仍无条件信任 CDN/LB 专用 Header。
  • 前置代理没有覆盖客户端传入的 Header,只是继续追加,后端应用又自行取了 XFF 最左侧值。
  • Nginx ACL 看的是 $remote_addr,应用 ACL 看的是未经校验的 X-Forwarded-For,两层判断口径不一致。
  • real_ip_recursive 与实际代理层数不匹配,或可信代理列表缺失/过宽。
  • 某个正则、命名 location、error_page 内部跳转或独立 server 块漏配了限制。
  • 配置了 satisfy any,导致 IP 白名单与认证条件变成“满足任意一个即可”。

可信边界比较明确时,可按下面方式配置。关键是只信任真实代理出口网段,而不是信任任意请求头:

# 仅填写实际 LB/反向代理地址
set_real_ip_from 10.20.0.0/16;
set_real_ip_from 192.0.2.20/32;

real_ip_header X-Forwarded-For;
real_ip_recursive on;

location ^~ /admin/ {
    allow 172.16.50.0/24;  # VPN/运维出口
    deny all;

    proxy_pass 
}

如果使用的是四层 LB 提供的 PROXY protocol,应直接按对应协议取地址,不要再混用可由客户端提交的 HTTP Header:

listen 443 ssl proxy_protocol;

set_real_ip_from 10.20.0.0/16;
real_ip_header proxy_protocol;

还要单独检查源站直连。很多绕过并不是 Header 欺骗,而是跳过 CDN/WAF 后直接访问 origin:

# 在授权的外部测试节点执行
curl -k -i \
  --resolve admin.example.com:443:203.0.113.10 \
  

# 源站侧检查监听和防火墙
ss -lntp
nft list ruleset
iptables -S

建议至少形成四组测试结果:外部直连、外部经代理、授权网段直连、授权网段经代理;每组都对照日志中的 remote、original 和转发头。若外部请求仅靠修改 Header 就从 403 变为可访问,或者源站直连能绕过边缘限制,基本就能确认信任边界存在问题。

最后不要只以 HTTP 200 判断成功,后台可能统一跳转登录页。应同时核对响应内容、应用审计日志和是否真正执行了受限接口。对于高价值后台,IP 白名单最好只作为附加控制,主控制仍放在 VPN、mTLS 或强认证上。

创建帐户或登录后发表意见

最近浏览 0

  • 没有会员查看此页面。