跳转到帖子

咖啡馆实战:反向代理真实 IP 配置错误导致后台越权的验证与修复

精选回复

发布于

背景

网站接入 CDN、负载均衡或 Nginx 反向代理后,常见做法是依据客户端 IP 限制运维后台,例如只允许办公网或内网地址访问。这里真正决定权限边界的,不是请求头里出现了什么 IP,而是源站是否只信任由指定代理节点写入的地址。

一次授权测试中,目标架构为“公网 CDN—Nginx 源站—后台应用”。源站直接暴露在公网,同时 Nginx 将任意来源提交的 X-Forwarded-For 解析为真实客户端地址。由于后台路径使用 allow/deny 做 IP 白名单,最终形成了可复现的权限绕过。

以下操作应仅在明确授权的域名、源站和测试窗口内执行。验证时使用测试账号与低频请求,不访问无关业务数据。

问题定位

初始现象是正常访问运维路径返回 403,但附带私网地址的 X-Forwarded-For 后,响应状态发生变化。先用最小请求确认差异,不进行目录扫描或批量尝试。

target='

# 基线请求:预期返回 403
curl -sk -o /dev/null -w 'baseline=%{http_code}\n' \
  "$target/ops/health"

# 边界验证:仅验证状态差异,不访问业务数据
curl -sk -o /dev/null -w 'forged_xff=%{http_code}\n' \
  -H 'X-Forwarded-For: 10.23.8.15' \
  "$target/ops/health"

# 保存响应头,观察是否经过预期 CDN 或负载均衡
curl -skD - -o /dev/null "$target/ops/health"

如果基线为 403,而伪造请求变为 200、302 或进入登录页面,说明 IP 边界可能受到了客户端可控请求头影响。单次状态变化还不能直接下结论,需要结合 Nginx 配置、源站入口和日志确认。

技术分析

高风险配置通常类似下面这样:

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

location /ops/ {
    allow 10.0.0.0/8;
    allow 192.168.50.0/24;
    deny all;
    proxy_pass 
}

ngx_http_realip_module 会根据受信代理范围和指定请求头改写连接中的客户端地址。若 set_real_ip_from 配置为 0.0.0.0/0,相当于信任所有访问者提供的 X-Forwarded-For。后续 access 模块执行 allow/deny 时,可能直接使用已被改写的地址,因此攻击者只需提交白名单内的私网 IP。

real_ip_recursive on 还会沿地址链递归寻找“最后一个非受信地址”。它本身不是漏洞,但在信任范围过宽时会放大问题。另一类常见场景是 CDN 回源正常,但源站安全组允许全网访问,测试者绕过 CDN 直接连接源站并自行设置 CDN 专用请求头。

服务端排查步骤

第一步是查看 Nginx 的最终生效配置,而不是只检查某个配置文件。include 引入的片段经常造成配置判断偏差。

# 输出完整生效配置并定位真实 IP、访问控制和监听项
sudo nginx -T 2>&1 | grep -nE \
  'set_real_ip_from|real_ip_header|real_ip_recursive|allow |deny |listen |proxy_protocol'

# 确认源站监听地址及端口
sudo ss -lntp | grep -E ':(80|443)\b'

# 检查主机侧入口规则
sudo nft list ruleset

# 修改配置后先做语法检查,再平滑加载
sudo nginx -t && sudo systemctl reload nginx

第二步是核对网络入口。即使 Nginx 的代理地址白名单正确,只要源站仍允许公网直连,应用若另外信任 X-Forwarded-For、X-Real-IP 或厂商自定义头,仍可能出现同类问题。需要同时检查云安全组、负载均衡后端规则、主机防火墙和 IPv6 入站策略。

第三步是确认应用层是否进行了第二次 IP 解析。常见框架提供 trusted proxies 或 proxy mode 配置,如果应用再次无条件取地址链最左侧值,Nginx 修复后依然可能被绕过。权限判断应使用框架解析后的可信连接地址,并将可信代理限定到实际反向代理网段。

补充日志留痕

默认访问日志只记录改写后的 remote_addr 时,会丢失直接连接源站的对端地址。建议同时记录原始连接地址、解析后的客户端地址和未经解析的转发头,并使用 JSON 转义,避免请求头中的控制字符破坏日志结构。

log_format boundary escape=json
  '{"time":"$time_iso8601",'
  '"peer":"$realip_remote_addr",'
  '"client":"$remote_addr",'
  '"xff":"$http_x_forwarded_for",'
  '"host":"$host",'
  '"request":"$request",'
  '"status":$status,'
  '"request_id":"$request_id"}';

access_log /var/log/nginx/boundary.json boundary;

其中 peer 表示建立 TCP 连接的对端,client 表示 realip 模块解析后的地址。排查时重点关注“公网 peer 直接访问源站,但 client 被解析成私网地址”的记录:

jq -c '
  select(
    (.client | test("^(10\\.|192\\.168\\.|172\\.(1[6-9]|2[0-9]|3[01])\\.)"))
    and
    (.peer | test("^(10\\.|192\\.168\\.|172\\.(1[6-9]|2[0-9]|3[01])\\.)") | not)
  )
' /var/log/nginx/boundary.json

该规则只适合作为初筛:代理节点可能使用公网地址,IPv6 也需要单独纳入判断。实际告警应维护可信代理地址集合,并关联 host、request_id、状态码和后台认证日志。

修复配置

修复原则是只信任实际反向代理节点,并在网络层禁止其他来源直连源站。不要为了省事填写整个互联网地址段,也不要盲目信任所有 RFC1918 私网,因为同一云网络中的其他租户或业务节点未必属于可信代理。

# 仅示例:替换为实际负载均衡或 CDN 回源地址段
set_real_ip_from 10.20.4.0/24;
set_real_ip_from 10.20.8.16/28;
set_real_ip_from 2001:db8:120::/48;

real_ip_header X-Forwarded-For;
real_ip_recursive on;

location /ops/ {
    allow 192.168.50.0/24;
    deny all;
    proxy_pass 
}

如果负载均衡支持 PROXY protocol,优先使用由传输层携带的源地址,同时仍需限制只有负载均衡节点可以连接该监听端口:

server {
    listen 443 ssl proxy_protocol;

    set_real_ip_from 10.20.4.0/24;
    real_ip_header proxy_protocol;

    # 其余 TLS 与站点配置省略
}

启用 PROXY protocol 前必须确认上下游同时支持,否则普通 HTTP 或 TLS 请求会被当作无效协议。变更时还要验证健康检查、回源端口和灰度节点,避免直接全量切换。

风险与修复建议

  • 在云安全组或主机防火墙中,只允许 CDN、负载均衡和管理跳板机访问源站端口,同时覆盖 IPv4 与 IPv6。
  • 精确维护 set_real_ip_from,建立 CDN 地址段更新流程;地址段变更后先生成差异、执行 nginx -t,再灰度加载。
  • 统一 Nginx 与应用框架的可信代理配置,避免两层以不同规则解析 X-Forwarded-For。
  • 后台管理接口不要只依赖 IP 白名单,还应启用独立身份认证、多因素认证和细粒度权限控制。
  • 日志同时保留原始对端地址和解析地址,并将命中后台路径、来源异常、连续 403 后转为 200 等事件接入告警。
  • 修复验证应同时覆盖正常 CDN 回源、直接源站访问、伪造转发头、IPv6 入口及多级代理地址链。

复测标准

测试项预期结果
通过可信代理访问普通页面业务正常,client 记录真实用户地址
公网直接访问源站网络层拒绝或连接超时
非可信来源伪造 X-Forwarded-For请求头不改变权限判断
非白名单用户访问后台路径稳定返回 403,不进入认证页面
可信办公网访问后台通过 IP 边界后仍需完成身份认证

总结

反向代理场景中的真实 IP 不只是日志字段,也可能直接参与访问控制。问题通常由三项条件叠加形成:源站可被绕过代理直连、可信代理范围配置过宽、后台将解析后的 IP 当作唯一权限依据。验证时应从最小状态差异入手,再结合生效配置、网络入口和双地址日志确认;修复则要同时收紧代理信任、源站入口和应用认证,不能只改一个请求头名称。

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

这类越权的根因通常不是 X-Forwarded-For 本身,而是后台把“客户端可控的请求头”当成了鉴权依据。修复时建议同时收紧代理链、网络入口和应用鉴权,不能只改一处。

如果是 Nginx 作为业务前的最后一层代理,set_real_ip_from 只能填写实际反向代理的固定地址或网段,不能写 0.0.0.0/0:

http {
    # 只信任真实 LB / CDN 回源网段
    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;

    log_format audit '$remote_addr original=$realip_remote_addr '
                     'xff="$http_x_forwarded_for" request="$request" status=$status';
}

这里要注意:开启 real_ip_recursive 后,Nginx 会从地址链右侧向左剥离受信代理,取第一个不受信地址。因此所有 set_real_ip_from 条目都必须来自可核验的代理节点;否则攻击者提交的地址仍可能被选成真实 IP。

边缘代理还应覆盖客户端传入的转发头,而不是原样透传。若入口 Nginx 直接接收公网请求,可以这样设置:

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_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

同时必须确保攻击者无法绕过入口直接访问后端。建议在安全组或防火墙层只允许代理节点访问业务端口:

# 示例:后端 8080 仅允许代理网段
iptables -A INPUT -p tcp --dport 8080 -s 10.20.0.0/16 -j ACCEPT
iptables -A INPUT -p tcp --dport 8080 -j DROP

验证时不要只观察页面结果,最好同时检查访问日志中的原始连接地址和解析后地址:

# 伪造内网、回环和多级地址链,均不应获得后台权限
curl -i  \
  -H 'X-Forwarded-For: 127.0.0.1'

curl -i  \
  -H 'X-Forwarded-For: 10.0.0.8, 127.0.0.1'

curl -i  \
  -H 'X-Real-IP: 127.0.0.1'

# 再尝试直连后端,预期应超时或被拒绝
curl -i 

最后,IP 白名单最多作为附加限制,不宜直接代表管理员身份。后台仍应要求独立登录态、角色校验和必要的二次认证。否则即使真实 IP 解析正确,同网段主机失陷、代理配置变更或 SSRF 到后台端口,仍可能再次形成越权路径。

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

最近浏览 0

  • 没有会员查看此页面。

SiteMap HACKHAT © 2026 All rights reserved.