跳转到帖子

渗透攻防实战:反向代理真实 IP 信任边界错误导致后台访问控制绕过

精选回复

发布于

背景

在一次授权内网渗透测试中,目标系统采用 Nginx 反向代理加后端管理服务的常见架构。管理接口通过来源 IP 限制访问,只允许办公网和运维跳板机网段进入。外部直接访问时返回 403,但修改 X-Forwarded-For 请求头后,管理接口可以正常打开。

问题不在后台认证本身,而在代理层和应用层对客户端 IP 的认定不一致:应用无条件信任用户可控的转发头,并将其作为访问控制依据。这类问题经常出现在多层代理、容器入口、CDN 回源和旧系统迁移场景中。

以下验证应限定在书面授权范围内。测试重点是确认权限边界是否可绕过,不应继续读取、修改与漏洞证明无关的数据。

问题定位

测试环境中的请求链路如下:

测试客户端 -> 边界 Nginx -> 内网管理应用
                           |
                           +-- 根据 client_ip 判断是否属于 10.20.0.0/16

首先建立三组对照请求,观察状态码、响应长度和重定向位置。不要仅凭页面内容判断,因为部分系统会统一返回登录页,但实际已经经过了 IP 校验。

curl -isk  \
  -o /dev/null -w 'base status=%{http_code} size=%{size_download} redirect=%{redirect_url}\n'

curl -isk  \
  -H 'X-Forwarded-For: 10.20.15.23' \
  -o /dev/null -w 'xff status=%{http_code} size=%{size_download} redirect=%{redirect_url}\n'

curl -isk  \
  -H 'X-Real-IP: 10.20.15.23' \
  -o /dev/null -w 'xri status=%{http_code} size=%{size_download} redirect=%{redirect_url}\n'

若基线请求为 403,而携带 X-Forwarded-For 后变为 200 或 302,应继续排除缓存、虚拟主机差异和上游网关自动改写。建议每次请求加入唯一标识,便于在各层日志中关联。

RID="pt-$(date +%s)-$RANDOM"
curl -isk  \
  -H "X-Request-ID: $RID" \
  -H 'X-Forwarded-For: 10.20.15.23' \
  -o /dev/null -w "$RID status=%{http_code}\n"

技术分析

X-Forwarded-For 并不是天然可信的身份信息。它只在请求确实来自受控代理,并且该代理会清理外部同名请求头时,才可以参与安全判断。

常见错误链路是:边界 Nginx 使用 proxy_add_x_forwarded_for,将客户端提交的伪造值保留在列表左侧;后端框架再取列表中的第一个地址作为真实客户端 IP。攻击者提交 X-Forwarded-For: 10.20.15.23 后,上游可能得到 10.20.15.23, 198.51.100.27。若应用取第一个值,IP 白名单便被绕过。

# 存在风险的代理配置
location / {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_pass 
}

另一类问题发生在 Nginx realip 模块配置过宽。若 set_real_ip_from 配置为 0.0.0.0/0,任何直连客户端都能通过指定请求头改变 $remote_addr,随后依赖 allow、deny 或日志变量的规则都会受到影响。

# 高风险配置,不应在生产环境使用
set_real_ip_from 0.0.0.0/0;
real_ip_header X-Forwarded-For;
real_ip_recursive on;

操作步骤

一、确认实际入口与代理层级

  • 解析域名并核对入口 IP,确认是否经过 CDN、负载均衡器、Ingress 或 WAF。
  • 检查是否存在可绕过边界设备的源站地址或内部监听端口。
  • 记录每一跳的源地址、目标地址以及请求头重写规则。
  • 对多级代理明确可信跳数,不要默认最左或最右地址一定是真实客户端。

在服务器侧可以结合监听状态和连接信息确认请求是否来自预期代理:

ss -lntp
ss -nt '( sport = :80 or sport = :443 )'
nginx -T 2>&1 | grep -nE 'real_ip|set_real_ip_from|proxy_set_header|allow|deny'

二、同时记录传输层地址与转发头

排查期间不要只记录处理后的 remote_addr。建议临时增加独立日志格式,同时保留 TCP 对端、realip 模块处理前地址和原始转发头。

log_format trust_boundary
  '$time_iso8601 rid=$request_id '
  'remote=$remote_addr realip_remote=$realip_remote_addr '
  'xff="$http_x_forwarded_for" xri="$http_x_real_ip" '
  'host="$host" request="$request" status=$status '
  'upstream=$upstream_addr upstream_status=$upstream_status';

access_log /var/log/nginx/trust_boundary.log trust_boundary;

随后按请求标识查询边界代理和应用日志:

grep -F 'pt-1710000000-12345' /var/log/nginx/trust_boundary.log
journalctl -u admin-app --since '10 minutes ago' | grep -F 'pt-1710000000-12345'

如果日志显示 remote 为外部测试地址,而应用审计日志中的 client_ip 已变成伪造内网地址,可以确认错误发生在应用解析逻辑。若 remote 本身已被改写,则应优先检查 Nginx realip 配置。

三、验证不同头部与地址格式

验证应采用最小请求集,重点观察系统究竟信任哪个字段,以及是否对地址列表进行了不安全解析。

测试项目的预期安全结果
无转发头建立基线使用 TCP 对端地址
X-Forwarded-For检查传统代理头非可信来源提交的值被覆盖或忽略
X-Real-IP检查单地址头非可信来源不能改变客户端身份
Forwarded检查标准头解析未启用时不参与授权判断
多个逗号分隔地址检查代理链解析方向只从受信代理边界向外解析

验证过程中不建议枚举大量私网地址。使用一个明确属于白名单的测试地址即可证明访问控制缺陷,并在报告中附带请求、响应状态、各层日志和配置证据。

四、区分身份认证与网络位置授权

进入管理入口不等于已经取得管理权限。报告中应分别描述两层影响:

  • 如果绕过后仅能看到登录页,影响是管理面暴露和第一层访问控制失效。
  • 如果系统使用来源 IP 自动登录、跳过多因素认证或授予额外角色,影响会明显扩大。
  • 如果后台仍有独立的强认证,应避免将问题夸大为直接接管。

安全配置示例

边界代理应覆盖外部提交的身份头,不要简单追加用户输入。单层 Nginx 直连应用时,可以明确将 TCP 对端地址传给后端:

location / {
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header Host $host;
    proxy_pass 
}

如果 Nginx 前面还有受控负载均衡器,应只信任负载均衡器的固定地址段,并确保源站端口无法被非代理网络直接访问:

# 仅示例,必须替换为实际负载均衡器网段
set_real_ip_from 10.10.8.0/24;
real_ip_header X-Forwarded-For;
real_ip_recursive on;

location /admin/ {
    allow 10.20.0.0/16;
    deny all;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_pass 
}

修改后先检查语法,再平滑加载配置:

nginx -t && nginx -s reload

风险与修复建议

  • 收敛可信代理范围:set_real_ip_from 只配置实际代理地址或最小网段,禁止使用 0.0.0.0/0。
  • 阻断源站直连:通过安全组、防火墙或网络策略,仅允许边界代理访问后端监听端口。
  • 统一头部处理:在最外层可信入口删除客户端提交的 X-Forwarded-For、X-Real-IP、Forwarded 等字段,再生成规范值。
  • 避免单独依赖来源 IP:管理面仍应启用独立身份认证、最小权限和多因素认证。
  • 保留原始证据:日志同时记录 TCP 对端地址、处理后的客户端地址、转发头、请求标识和上游地址。
  • 配置变更后回归:重复基线、伪造头和源站直连测试,确认外部请求均无法改变授权结果。

主机侧还可以增加一条明确的网络限制。以下示例仅表达原则,实际规则应结合现有防火墙体系,避免直接复制导致业务中断:

# 仅允许反向代理 10.10.8.15 访问后端 8080 端口
iptables -A INPUT -p tcp -s 10.10.8.15 --dport 8080 -j ACCEPT
iptables -A INPUT -p tcp --dport 8080 -j DROP

日志留痕与检测

这类绕过通常会留下地址不一致特征。可以关注以下情况:

  • 公网 TCP 对端携带私网 X-Forwarded-For。
  • 同一来源短时间提交多个不同的客户端地址。
  • 转发头包含异常数量的地址或空白项。
  • 管理接口出现外部来源的 403 后紧接 200 或 302。
  • 应用日志与代理日志对同一请求记录了不同的客户端 IP。

检测规则应以可信代理清单为基础。仅判断出现私网地址容易误报,因为企业代理和混合云链路本身可能合法携带私网地址。

总结

反向代理场景中的真实 IP 不是简单取一个请求头,而是一条需要明确边界的信任链。渗透验证时应通过对照请求、唯一请求标识和多层日志确认问题发生位置;修复时则要同时处理可信代理范围、头部覆盖、源站隔离和后台强认证。只修应用解析或只改 Nginx 其中一处,往往仍会在另一条入口上留下绕过条件。

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

这类绕过的关键通常不在“是否读取 X-Forwarded-For”,而在于:应用把谁当成可信代理,以及源站是否允许绕过代理直接访问。建议按请求链路逐层确认,不要只改后台 IP 白名单。

1. 边缘代理必须覆盖客户端传入的转发头,不能继续透传:

location / {
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_set_header X-Real-IP       $remote_addr;
    proxy_set_header Host            $host;
    proxy_pass 
}

如果前面确实还有一层受控负载均衡,可以使用 $proxy_add_x_forwarded_for 保留链路;但最外层入口仍应先清除外部请求自带的同名头。否则攻击者提交的 X-Forwarded-For: 127.0.0.1 可能进入可信地址链。

2. Nginx 的真实 IP 模块只信任明确的代理地址段:

set_real_ip_from 10.20.0.0/16;
set_real_ip_from 192.0.2.10/32;
real_ip_header X-Forwarded-For;
real_ip_recursive on;

不要配置 set_real_ip_from 0.0.0.0/0,也不要为了兼容容器网络直接信任整个内网。开启 real_ip_recursive 后,Nginx 会从地址链右侧向左跳过可信代理,取第一个不可信地址;因此代理列表缺失或范围过大都会导致结果错误。

3. 先记录“连接来源”和“还原后的来源”,便于定位是哪一层改写:

log_format iptrace '$remote_addr realip_remote=$realip_remote_addr '
                   'xff="$http_x_forwarded_for" host="$host" '
                   'request="$request" status=$status';

access_log /var/log/nginx/iptrace.log iptrace;

其中 $realip_remote_addr 是建立 TCP 连接的原始对端,$remote_addr 是 realip 模块处理后的地址。可以结合下面的命令检查:

nginx -T | grep -nE 'set_real_ip_from|real_ip_header|real_ip_recursive|proxy_set_header'
tail -f /var/log/nginx/iptrace.log

4. 用两组请求做回归测试:

# 正常外部请求,伪造头不应获得后台权限
curl -ki  \
  -H 'X-Forwarded-For: 127.0.0.1' \
  -H 'X-Real-IP: 127.0.0.1'

# 检查重复头、逗号地址链是否被错误取首项
curl -ki  \
  -H 'X-Forwarded-For: 127.0.0.1, 203.0.113.20'

预期结果应是统一拒绝,日志中的最终客户端地址不能变成回环或后台白名单地址。同时要覆盖 IPv6、重复请求头,以及 CDN/负载均衡健康检查路径。

5. 封死源站直连:源站安全组或防火墙只允许反向代理网段访问业务端口。否则即使转发头处理正确,攻击者仍可能通过源站 IP 绕开边缘层。

# 示例:仅允许代理节点访问源站 8080
iptables -A INPUT -p tcp -s 192.0.2.10 --dport 8080 -j ACCEPT
iptables -A INPUT -p tcp --dport 8080 -j DROP

另外,后台管理不建议只依赖客户端 IP。至少再叠加独立认证、VPN/mTLS 或应用层权限校验。IP 白名单适合作为附加限制,不应成为唯一授权条件。如果链路使用 PROXY protocol,还要统一在监听端和负载均衡端启用,避免同时混用 PROXY protocol 与未经约束的转发头。

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

最近浏览 0

  • 没有会员查看此页面。