发布于21小时前21小时 背景网站接入 CDN、WAF 或 Nginx 反向代理后,源站通常会从 X-Forwarded-For、X-Real-IP 或厂商专用请求头恢复访客地址。这里最容易出现的问题不是“拿不到真实 IP”,而是源站无条件相信客户端可控的请求头。后果包括限速绕过、后台 IP 白名单失效、审计日志被污染,以及应用层风控错误归因。本文以已获授权的网站与服务器为对象,给出一套低影响验证流程。场景为:公网入口经过 CDN,源站使用 Nginx,后端应用监听 127.0.0.1:8080。测试重点是代理信任边界、源站暴露面和日志链路,不涉及绕过第三方站点控制。问题定位先画清请求链路:客户端 → CDN/WAF → 源站 Nginx → 应用。每一跳都可能追加或覆盖转发头。排查时需要回答三个问题:源站 80/443 是否只能由可信代理网段访问;Nginx 只信任了哪些代理地址,是否错误配置为 0.0.0.0/0;应用和访问日志最终使用的是连接源地址,还是未经校验的请求头。不要一开始就改配置。先备份 Nginx 配置与日志格式,并记录测试时间、测试出口 IP、域名和唯一标记,便于在 CDN、源站及应用日志中关联同一次请求。授权验证步骤第一步,从正常入口发送带唯一标记的基线请求,再伪造常见真实 IP 请求头。这里使用文档保留地址 198.51.100.23,避免日志中混入真实第三方地址。MARK=realip-test-$(date +%s) curl -sk -D- \ -H "X-Audit-Mark: $MARK-baseline" \ curl -sk -D- \ -H "X-Audit-Mark: $MARK-spoof" \ -H "X-Forwarded-For: 198.51.100.23" \ -H "X-Real-IP: 198.51.100.23" \ 198.51.100.23,说明链路中至少有一层直接采用了客户端输入。仅看到原始请求头并不能直接判定漏洞,关键是该值是否进入鉴权、白名单、限速或审计决策。sudo grep -R "$MARK" /var/log/nginx/ /var/log/myapp/ 2>/dev/null sudo nginx -T | grep -nE 'real_ip|set_real_ip_from|log_format|proxy_set_header' sudo ss -lntp | grep -E ':(80|443|8080)\b' sudo nft list ruleset第二步,在资产所有者允许的测试出口检查源站是否可被直接访问。不要进行网段扫描,只对资产清单中确认的源站地址发起单次请求。使用 resolve 固定域名与源站 IP,可同时保持正确的 Host 和 TLS SNI。ORIGIN_IP=203.0.113.10 curl -skI --resolve example.test:443:$ORIGIN_IP \ -H "X-Audit-Mark: $MARK-origin" \ CDN/WAF 到达源站。此时要继续确认源站防火墙、云安全组、负载均衡监听和回源鉴权,而不是只看 Nginx。技术分析:Nginx 的正确语义ngx_http_realip_module 只有在连接来源命中 set_real_ip_from 时,才会依据 real_ip_header 改写 $remote_addr。real_ip_recursive on 会沿地址链从右向左跳过可信代理,选出第一个不可信地址。可信范围必须来自 CDN 官方当前回源网段或自有负载均衡地址,不能为了省事写成全网。# 仅示例,生产环境应由 CDN 官方地址清单生成并定期更新 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; # 向后端传递经过 Nginx 信任链计算后的结果 proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host;如果 CDN 使用专用真实 IP 头,应按厂商文档配置 real_ip_header,并确认 CDN 会在边缘覆盖客户端同名头,而不是简单透传。多层代理场景还要把每一层真实的出口地址加入信任范围;漏配会得到代理地址,过度配置则会信任攻击者。日志设计与证据保留排障期间建议同时记录改写后的客户端地址、原始连接地址和原始转发头。$remote_addr 是 realip 模块处理后的值,$realip_remote_addr 保留改写前的连接来源。两者同时存在,才能判断请求是否确实来自 CDN。log_format proxy_audit escape=json '{"time":"$time_iso8601",' '"request_id":"$request_id",' '"client_ip":"$remote_addr",' '"peer_ip":"$realip_remote_addr",' '"xff":"$http_x_forwarded_for",' '"host":"$host","request":"$request",' '"status":$status,"mark":"$http_x_audit_mark"}'; access_log /var/log/nginx/proxy_audit.log proxy_audit;上线前先执行语法检查和配置差异检查,再平滑重载。不要直接重启导致连接中断。sudo nginx -t sudo nginx -T > /tmp/nginx-effective.conf sudo systemctl reload nginx sudo journalctl -u nginx --since "10 minutes ago" --no-pager日志中的 X-Forwarded-For 仍是不可信原始输入,不应直接作为封禁依据。建议以 request_id 关联 CDN 日志、Nginx 审计日志和应用日志,并设置访问权限、轮转与保留周期。日志系统若支持结构化字段,应把 client_ip、peer_ip 和 xff 分开存储,避免后续解析歧义。权限边界与修复建议网络层:源站安全组仅允许 CDN/WAF 回源网段访问 80/443;管理端口仅允许堡垒机或管理 VPN。IPv4 与 IPv6 规则要同时检查。代理层:set_real_ip_from 使用最小可信网段,禁止 0.0.0.0/0 和 ::/0;通过自动化任务同步厂商网段,但变更前做格式校验和审批。回源认证:在网络白名单之外,可增加 CDN 到源站的双向 TLS 或不可由公网客户端设置的回源认证机制。静态共享头只能作为补充,且要定期轮换。应用层:应用只读取反向代理规范化后的地址;后台白名单、登录限速和风控不要直接消费用户提交的 X-Forwarded-For。权限控制:Nginx 配置、日志目录与发布脚本采用最小权限;应用账号不应拥有修改代理配置或清理审计日志的权限。变更验证:修复后重复基线、伪造头、源站直连三组测试,并确认正常用户地址识别、IPv6、WebSocket 与健康检查未受影响。判定标准现象风险判断处理优先级公网可直连源站,且可绕过边缘访问控制边界绕过成立高任意客户端头可改变鉴权或白名单判断信任边界失效高仅日志记录原始伪造头,决策仍使用可信地址日志污染与误判风险中源站仅允许可信代理,日志同时保留 peer/client 地址边界基本合理继续做回归测试总结CDN 和反向代理场景的核心不是“使用哪个真实 IP 请求头”,而是谁有资格提供这个头。排查应从源站暴露面开始,再核对 Nginx 的可信代理范围、地址链递归规则、应用取值方式和日志证据。修复也不能只改一行 real_ip_header:网络访问控制、代理规范化、应用决策与审计留痕需要形成完整闭环。 网络请求、日志与边界流量分析示意 服务器、云资产与权限边界梳理示意
21小时前21小时 这类验证的重点不是“能不能读到 X-Forwarded-For”,而是证明:只有可信 CDN 节点才有资格改写客户端地址,客户端无法绕过 CDN 或伪造这条链路。 建议先做两组负向测试。第一组直连源站并伪造头,预期应当在网络层被拒绝,而不是应用返回一个伪造后的 IP: curl -vk --resolve \ \ -H 'X-Forwarded-For: 1.1.1.1' \ -H 'CF-Connecting-IP: 1.1.1.1' 源站的 80/443 入站规则只放行 CDN 官方网段;管理和监控流量另开独立白名单。不要只依赖隐藏源站域名,因为历史 DNS、证书透明度和邮件记录都可能泄露源站地址。 第二组经 CDN 请求,主动注入冲突值: curl -sk \ -H 'X-Forwarded-For: 1.1.1.1, 2.2.2.2' \ -H 'CF-Connecting-IP: 3.3.3.3' 应用最终识别到的地址应是测试机公网 IP,而不是上述任意伪造值。还应覆盖空值、多个逗号、IPv6、带端口、超长头等情况,确认异常输入不会造成 500 或污染结构化日志。 以 Nginx 为例,若使用 CDN 提供的专用客户端地址头,可以这样收紧信任边界: # 仅填写 CDN 官方公布并自动同步的出口网段 set_real_ip_from 203.0.113.0/24; set_real_ip_from 2001:db8:1234::/48; real_ip_header CF-Connecting-IP; real_ip_recursive off; log_format edge escape=json '{"time":"$time_iso8601",' '"client_ip":"$remote_addr",' '"peer_ip":"$realip_remote_addr",' '"xff":"$http_x_forwarded_for",' '"edge_ip_header":"$http_cf_connecting_ip",' '"request_id":"$request_id",' '"method":"$request_method",' '"uri":"$request_uri",' '"status":$status}'; access_log /var/log/nginx/edge_access.json edge; 这里应同时记录两个地址:$realip_remote_addr 是实际 TCP 对端,便于证明请求确实来自 CDN;$remote_addr 是完成可信代理解析后的客户端地址。原始转发头也可以留存用于取证,但不要让应用再次自行解析它,否则 Nginx 与应用可能形成两套信任规则。 如果必须使用 X-Forwarded-For,要先确认 CDN 的行为究竟是覆盖、追加还是原样透传,再决定是否开启 real_ip_recursive。不能照抄配置;尤其要避免信任 0.0.0.0/0 或整个内网段。CDN 网段建议由定时任务从官方接口拉取,校验格式后生成独立 include 文件,执行 nginx -t 成功才原子替换并 reload。 最后检查日志闭环:在 CDN、源站和应用层统一透传请求 ID,随机抽样核对同一请求的边缘时间、TCP 对端、解析后 IP、状态码是否一致;对“源站出现非 CDN 对端”“可信头缺失”“同一请求 ID 对应多个客户端地址”设置告警。这样验证的不只是页面显示结果,也包括绕过路径、配置漂移和事后追溯能力。 创建帐户或登录后发表意见 注册帐户 立刻登录 分享 https://www.hackhat.org/Hacker-topic/27122.html/ 更多分享选项… 粉丝