发布于15小时前15小时 背景网站接入 CDN、WAF、Nginx 反向代理或自建加速器后,应用获取到的客户端地址通常不再是用户真实 IP,而是上一层代理节点的地址。为恢复真实 IP,很多站点会直接信任 X-Forwarded-For、X-Real-IP、CF-Connecting-IP 等请求头。问题在于:如果源站可以被公网直接访问,或者 Nginx 将任意客户端提交的转发头原样传给后端,攻击者就可能伪造来源地址。这类配置缺陷经常影响后台 IP 白名单、接口限流、审计日志和封禁策略。下面以授权测试环境中的“CDN + Nginx + Web 应用”架构为例,说明如何验证信任边界。典型场景测试目标的后台接口只允许内网地址访问,应用代码根据 REMOTE_ADDR 或代理头进行判断:def allow_admin(request): client_ip = request.headers.get("X-Forwarded-For", request.remote_addr) client_ip = client_ip.split(",")[0].strip() return client_ip.startswith("10.") or client_ip == "127.0.0.1"这段逻辑存在两个问题:应用无条件信任用户可控的请求头;多级代理链中直接取最左侧地址,却没有确认该地址是否由可信代理追加。常见的错误 Nginx 配置如下:location / { proxy_set_header Host $host; proxy_set_header X-Real-IP $http_x_real_ip; proxy_set_header X-Forwarded-For $http_x_forwarded_for; proxy_pass }这里的 $http_x_forwarded_for 和 $http_x_real_ip 都来自客户端请求。代理没有重建可信头,而是将攻击者提供的值直接交给应用。问题定位授权测试时,先确认实际访问链路,不要直接尝试绕过。重点检查域名入口、源站暴露情况、代理层数量,以及应用最终使用哪个字段作为客户端地址。通过 DNS 记录和资产清单确认域名是否指向 CDN 或负载均衡。检查源站防火墙、安全组是否只允许代理节点地址段访问。查看 Nginx access_log 中的 remote_addr、http_x_forwarded_for 和 realip_remote_addr。检查应用框架是否启用了 ProxyFix、ForwardedHeaders 或 trust proxy。确认后台白名单、限流和封禁逻辑引用的是原始连接地址还是转发头。建议临时增加独立日志格式,将代理解析前后的地址同时记录:log_format proxy_audit '$time_iso8601 ' 'remote=$remote_addr ' 'realip=$realip_remote_addr ' 'xff="$http_x_forwarded_for" ' 'xri="$http_x_real_ip" ' 'host=$host request="$request" status=$status'; access_log /var/log/nginx/proxy_audit.log proxy_audit;授权验证步骤第一步是在测试接口上发送普通请求,记录返回状态和服务端日志:curl -k -i -k -i \ -H 'X-Forwarded-For: 127.0.0.1' curl -k -i \ -H 'X-Real-IP: 10.10.10.20' curl -k -i \ -H 'Forwarded: for=127.0.0.1;proto=https;host=test.example.com'如果普通请求返回 403,而添加某个头后返回 200,不能立即认定为完整鉴权绕过。还需要核对代理日志和应用日志,确认变化确实来自 IP 判断,而不是缓存、Host 路由或其他条件。第三步,验证多级代理链处理是否正确:curl -k -i \ -H 'X-Forwarded-For: 127.0.0.1, 198.51.100.25'若应用固定取最左侧值,攻击者可将伪造地址放在链首。若应用固定取最右侧值,在存在多个可信代理时也可能错误识别 CDN 节点或边缘负载均衡地址。正确做法是从连接来源开始,逐级剔除已配置的可信代理,只接受可信设备追加的地址。源站直连检查即使域名入口配置正确,只要源站 IP 可以从公网直连,攻击者就可能绕过 CDN/WAF,并自行构造代理头。测试时可以在已知源站地址的前提下,通过 Host 和 resolve 参数验证入口限制:curl -k -i --resolve test.example.com:443:203.0.113.10 \ \ -H 'X-Forwarded-For: 127.0.0.1'这里应重点观察三个结果:TCP/TLS 是否可建立、虚拟主机是否正常响应、源站是否接受非代理节点来源。若源站对公网返回完整业务页面,说明加速器并未形成真正的入口边界。安全配置示例Nginx 应仅信任明确的代理地址段,并使用 realip 模块解析客户端地址。地址段应来自实际部署清单或服务商正式文档,不应使用 0.0.0.0/0。set_real_ip_from 192.0.2.0/24; set_real_ip_from 2001:db8:100::/48; real_ip_header X-Forwarded-For; real_ip_recursive on; location / { proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_pass }$proxy_add_x_forwarded_for 会在现有链后追加当前客户端地址,但它并不能单独解决伪造问题。安全性依赖 set_real_ip_from 是否仅包含可信代理,以及源站是否拒绝其他来源。如果后端不需要保留完整代理链,可以在边缘代理处直接覆盖外部提交的头:proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header Forwarded "";对于 Express,应限制可信代理数量或地址段,避免直接使用 app.set('trust proxy', true):app.set('trust proxy', ['loopback', '192.0.2.0/24']);Flask/Werkzeug 的 ProxyFix 同样需要按实际代理层数配置,不能因为部署了 CDN 就盲目相信所有转发字段:from werkzeug.middleware.proxy_fix import ProxyFix app.wsgi_app = ProxyFix( app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_port=0, x_prefix=0 )日志留痕与排查处理此类事件时,不要只检索包含 127.0.0.1 的 X-Forwarded-For。攻击者也可能使用内网地址、云元数据地址或与代理节点相似的地址。建议关联原始连接地址、转发链、请求路径、认证账号和状态码。grep -E 'xff="(127\.0\.0\.1|10\.|172\.(1[6-9]|2[0-9]|3[01])\.|192\.168\.)' \ /var/log/nginx/proxy_audit.log awk '$0 ~ /status=200/ && $0 ~ /\/internal\// {print}' \ /var/log/nginx/proxy_audit.log还应检查同一 remote_addr 是否在短时间内提交大量不同的 X-Forwarded-For,以及同一账号是否突然从多个逻辑来源地址访问。日志至少保留原始 socket 地址和解析后的业务地址,不能只保留其中一个。字段用途remote_addr记录与 Nginx 建立连接的直接来源realip_remote_addr保留 realip 模块改写前的地址http_x_forwarded_for保留完整转发链,供事件回溯request_id关联代理、应用和网关日志upstream_addr确认请求实际转发到哪个后端实例风险与修复建议不要使用客户端可控的 IP 头作为唯一鉴权依据。后台接口应同时要求身份认证和细粒度授权。在安全组或防火墙层限制源站入口,只允许 CDN、WAF、负载均衡及运维网络访问。代理层统一删除或覆盖外部提交的 Forwarded、X-Real-IP 和 X-Forwarded-For。可信代理必须使用明确地址段,并建立更新流程,避免地址范围变更后长期失效。限流、封禁和审计应使用同一套经过验证的客户端地址解析逻辑。修复后重新执行普通请求、伪造头、多级代理链和源站直连四组回归测试。IP 白名单适合作为附加限制,不适合作为后台接口的唯一认证机制。代理头是否可信,不取决于字段名称,而取决于该字段是否由受控代理在受控链路中生成。总结网站接入加速器后,真正需要保护的是“客户端到代理、代理到源站、源站到应用”三段链路的信任边界。排查时应同时验证源站暴露、代理头重建、框架信任配置和日志完整性。修复重点不是简单屏蔽某个请求头,而是限制源站入口、明确可信代理、统一地址解析,并让敏感接口继续依赖可靠的身份认证与权限控制。 服务器、云资产与权限边界梳理示意 网络请求、日志与边界流量分析示意
13小时前13小时 这类问题建议把“鉴权链路”和“日志取证链路”分开排查,重点确认源站是否错误信任了客户端可控的转发头。 先绕过加速器直连源站,对同一请求分别测试,并显式伪造常见头部: curl -i curl -i --resolve example.com:443:源站IP curl -i \ -H 'X-Forwarded-For: 127.0.0.1' \ -H 'X-Real-IP: 127.0.0.1' \ -H 'X-Original-URL: /admin' \ -H 'X-Rewrite-URL: /admin' 如果只有携带某个头时能绕过,通常是应用把 X-Forwarded-For 当成可信来源,或者代理与后端对重写前后的 URI 理解不一致。边缘层应覆盖这些头,而不是把客户端值继续向后拼接: location / { proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Original-URL ""; proxy_set_header X-Rewrite-URL ""; proxy_pass } 若前面还有可信 CDN,不能简单信任任意来源的 X-Forwarded-For。应只信任 CDN 官方出口网段,并保证源站防火墙只允许这些网段访问: # nginx set_real_ip_from 203.0.113.0/24; # 替换为实际 CDN 网段 real_ip_header X-Forwarded-For; real_ip_recursive on; # 防火墙思路:先放行管理入口/CDN,再拒绝公网直连源站 iptables -A INPUT -p tcp --dport 443 -s 203.0.113.0/24 -j ACCEPT iptables -A INPUT -p tcp --dport 443 -j DROP 日志失真往往是日志只记录了“解析后的客户端 IP”,却没保留 TCP 对端和原始头。排查阶段建议同时记录三者,并加请求 ID 串联 CDN、代理和应用日志: log_format trace escape=json '{"time":"$time_iso8601",' '"request_id":"$request_id",' '"peer":"$remote_addr",' '"xff":"$http_x_forwarded_for",' '"realip":"$http_x_real_ip",' '"host":"$host",' '"request":"$request",' '"uri":"$uri",' '"upstream":"$upstream_addr",' '"status":$status,' '"upstream_status":"$upstream_status"}'; access_log /var/log/nginx/access_trace.log trace; proxy_set_header X-Request-ID $request_id; 还要检查加速器缓存规则:登录态、管理接口和带 Authorization/Cookie 的请求不应进入公共缓存,否则可能表现为“鉴权绕过”,实际是未授权用户命中了已缓存的授权响应。Nginx 可先做保守限制: proxy_no_cache $http_authorization $cookie_session; proxy_cache_bypass $http_authorization $cookie_session; location ^~ /admin/ { proxy_no_cache 1; proxy_cache_bypass 1; add_header Cache-Control "private, no-store" always; } 最后建议核对代理层与应用层实际用于鉴权的路径:同时记录 $request_uri、$uri 和应用收到的路由,重点测试 URL 编码、重复斜杠、分号参数及路径归一化差异。修复原则是鉴权放在最终路由解析之后,并且源站自身必须执行鉴权,不能只依赖加速器上的访问规则。
创建帐户或登录后发表意见