发布于3小时前3小时 背景网站接入 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 当作唯一权限依据。验证时应从最小状态差异入手,再结合生效配置、网络入口和双地址日志确认;修复则要同时收紧代理信任、源站入口和应用认证,不能只改一个请求头名称。 服务器、云资产与权限边界梳理示意 网络请求、日志与边界流量分析示意
1小时前1小时 这类越权的根因通常不是 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 到后台端口,仍可能再次形成越权路径。
创建帐户或登录后发表意见