发布于36分钟前36分钟 背景网站接入 CDN、WAF 或 Nginx 加速节点后,源站通常不再直接面对客户端。这个架构能改善访问速度并降低源站暴露面,但也容易引入一个权限边界问题:源站是否无条件信任代理传来的 X-Forwarded-For、X-Real-IP 或 CDN 专用请求头。在一次已授权的网站安全检查中,业务使用“CDN—Nginx—应用”三级架构,后台管理接口依据客户端 IP 放行。测试发现源站仍可通过公网访问,而且 Nginx 信任任意来源提交的 X-Forwarded-For。攻击者不需要突破 CDN,只要直连源站并伪造请求头,就可能绕过应用层 IP 白名单。同时,访问日志记录的也是伪造地址,给事件追踪造成干扰。以下操作应仅用于自有系统或明确授权的测试环境。验证重点是代理信任边界和日志完整性,不涉及破坏性利用。问题定位先确认实际链路,不要直接修改 real_ip 配置。需要回答四个问题:源站能否绕过 CDN 直接访问;哪一层负责覆盖客户端 IP 请求头;Nginx 信任了哪些代理地址;应用读取的是连接源地址还是可控请求头。可以从外部测试机对域名和已授权的源站地址分别发起请求。使用固定路径与唯一追踪值,便于在各层日志中关联。TRACE_ID="audit-$(date +%Y%m%d%H%M%S)" # 正常经过域名/CDN访问 curl -sk -D - \ -H "X-Audit-ID: ${TRACE_ID}-cdn" \ -o /dev/null # 已确认授权后,直连源站并保留原始 Host curl -sk -D - \ --resolve \ -H "X-Audit-ID: ${TRACE_ID}-origin" \ -H "X-Forwarded-For: 127.0.0.1" \ -H "X-Real-IP: 127.0.0.1" \ -o /dev/null如果直连源站仍能得到正常业务响应,需要继续检查源站安全组、防火墙和虚拟主机默认行为。如果应用或日志把客户端识别为 127.0.0.1,则说明代理头信任边界存在缺陷。技术分析Nginx 中的 remote_addr 默认来自 TCP 连接。启用 ngx_http_realip_module 后,Nginx 会在连接来源属于 set_real_ip_from 指定的可信范围时,使用 real_ip_header 中的地址重写 remote_addr。常见错误是配置 set_real_ip_from 0.0.0.0/0,或者应用直接读取 X-Forwarded-For,而没有判断请求是否来自可信反向代理。X-Forwarded-For 是普通 HTTP 请求头,客户端可以自行提交;只有最后一跳代理主动覆盖该头,并且源站仅允许可信代理访问时,它才适合参与访问控制和审计。检查项危险表现建议状态源站入口公网任意地址可直接访问仅允许 CDN、WAF 或负载均衡地址段代理信任信任 0.0.0.0/0只配置明确维护的代理网段请求头处理直接透传客户端提交的 X-Forwarded-For边缘代理覆盖,源站按可信链解析后台授权仅依赖请求头中的 IP身份认证、MFA、网络边界共同控制访问日志只记录重写后的 remote_addr同时记录连接地址、解析地址和代理链操作步骤第一步,在源站确认真实配置加载结果。检查运行配置比只看某个配置文件更可靠,因为 include 文件可能覆盖前面的设置。# 输出完整生效配置,并定位真实 IP 与日志相关指令 sudo nginx -T 2>&1 | grep -nE \ 'set_real_ip_from|real_ip_header|real_ip_recursive|log_format|access_log|proxy_set_header' # 确认 Nginx 是否编译 realip 模块 nginx -V 2>&1 | grep -o 'http_realip_module' # 查看当前监听端口和进程 sudo ss -lntp | grep -E ':80|:443'第二步,检查源站最近的连接来源。若架构设计要求所有流量经过 CDN,而日志中存在大量非 CDN 地址直接连接源站,应先判断是否为监控、办公出口或其他合法代理,再处理异常来源。# 统计 Nginx 日志首列中的高频地址,仅适用于首列仍为连接地址的格式 sudo awk '{count[$1]++} END {for (ip in count) print count[ip], ip}' \ /var/log/nginx/access.log | sort -nr | head -30 # 从系统连接层观察当前 443 端口对端地址 sudo ss -nt state established '( sport = :443 )'第三步,建立不会混淆连接地址与解析地址的审计日志。realip 模块启用后,remote_addr 可能已经被重写;realip_remote_addr 用于保留原始 TCP 对端地址。log_format proxy_audit escape=json '{"time":"$time_iso8601",' '"request_id":"$request_id",' '"peer_ip":"$realip_remote_addr",' '"client_ip":"$remote_addr",' '"xff":"$http_x_forwarded_for",' '"x_real_ip":"$http_x_real_ip",' '"host":"$host","request":"$request",' '"status":$status,"bytes":$body_bytes_sent,' '"ua":"$http_user_agent"}'; access_log /var/log/nginx/proxy_audit.log proxy_audit;第四步,收紧可信代理范围。下面的地址仅为文档示例,实际部署时应使用服务商发布并持续维护的地址段,不能照抄。# 只信任明确的 CDN/WAF 出口网段 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; # 修改后先验证,再平滑加载 sudo nginx -t && sudo systemctl reload nginxreal_ip_recursive on 适用于存在多层可信代理的情况。Nginx 会沿地址链向前查找最后一个不受信任的地址。如果代理链结构不固定,或者上游不会覆盖客户端传入的同名请求头,不应仅靠递归解析解决问题;应先在最外层代理删除或重建 X-Forwarded-For。源站入口与应用权限边界修正请求头解析仍不够。更稳妥的做法是在云安全组、主机防火墙或负载均衡层限制源站入口,只允许 CDN、WAF、运维跳板和必要监控节点访问。这样即使 Nginx 配置将来被误改,外部客户端也难以直连源站伪造代理头。后台管理接口不应只使用 IP 白名单作为身份认证。IP 适合做附加限制,但还应保留独立账号、MFA、短会话、细粒度角色和操作审计。应用侧若必须使用客户端 IP,应优先读取由受控代理或 Web 容器提供的规范化字段,不要直接读取任意 HTTP 头。以应用和 Nginx 同机为例,反向代理可以显式重建传给应用的头:location / { proxy_set_header Host $host; proxy_set_header X-Request-ID $request_id; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header X-Forwarded-Proto $scheme; proxy_pass }这里使用覆盖而不是简单追加,目的是避免应用收到客户端预先构造的地址链。若后端还经过其他受控代理,需要按实际拓扑设计头部重建规则,不能机械套用。日志验证与异常排查变更后重新执行经过 CDN和直连源站的两组请求。预期结果是:经过 CDN 的请求中,peer_ip 为可信代理地址,client_ip 为解析后的客户端地址;未经过可信代理的请求中,client_ip 应保持实际连接地址,伪造的 X-Forwarded-For 只能作为原始字段留存,不能影响权限判断。# 按审计标识关联验证记录 sudo grep 'audit-' /var/log/nginx/proxy_audit.log | tail -20 # 检查配置变更后的错误日志 sudo journalctl -u nginx --since '-15 min' --no-pager sudo tail -n 100 /var/log/nginx/error.log可以重点告警以下情况:peer_ip 不属于可信代理网段,但请求中携带 X-Forwarded-For 或 CDN 专用客户端地址头。同一请求的 peer_ip、client_ip 和应用记录地址无法按既定链路解释。源站收到非代理来源对管理路径、登录路径或敏感 API 的请求。X-Forwarded-For 地址链过长、包含非法字符、私网地址异常穿插或短时间频繁变化。CDN 回源请求缺少双方约定的 mTLS 身份、签名头或其他回源认证特征。风险与修复建议网络层先行:关闭不必要的源站公网入口,安全组仅放行受控代理网段,并建立地址段更新流程。代理层最小信任:禁止 set_real_ip_from 0.0.0.0/0,只信任明确的 CDN、WAF 和负载均衡出口。边缘层覆盖请求头:删除客户端提交的内部鉴权头和客户端地址头,再由边缘节点重新生成。应用层避免单因子授权:IP 白名单只作为附加条件,管理面应启用强认证、MFA 和角色控制。审计字段分离:同时记录原始连接地址、规范化客户端地址、完整代理链、请求 ID 和虚拟主机。变更后回归:分别验证正常访问、直连源站、伪造请求头、IPv6、多层代理和代理故障切换场景。保留回滚:修改前保存 Nginx 完整生效配置,先执行 nginx -t,再平滑加载并观察错误率。总结网站接入服务器加速器、CDN 或 WAF 后,真正需要保护的不只是源站地址,而是从边缘代理到应用的整条信任链。排查时应同时查看网络入口、Nginx 生效配置、请求头重建方式、应用取值逻辑和日志字段。修复的核心原则是:只信任明确代理、禁止绕过代理、保留原始连接证据,并且不让可伪造的客户端地址头单独参与权限判断。 网络请求、日志与边界流量分析示意 服务器、云资产与权限边界梳理示意
21分钟前21分钟 这个场景里最容易踩坑的不是“拿不到真实 IP”,而是无条件信任客户端传入的 X-Forwarded-For。建议先明确权限边界:只有反向代理/加速器的出口地址有资格声明真实客户端 IP,源站不得直接暴露给公网。 Nginx 源站可以按可信代理网段解析,并保留连接层地址用于审计: set_real_ip_from 10.20.0.0/16; # 自建反代网段 set_real_ip_from 203.0.113.0/24; # 加速器官方出口段,按实际填写 real_ip_header X-Forwarded-For; real_ip_recursive on; log_format trace escape=json '{"time":"$time_iso8601",' '"request_id":"$request_id",' '"client_ip":"$remote_addr",' '"peer_ip":"$realip_remote_addr",' '"xff":"$http_x_forwarded_for",' '"method":"$request_method",' '"uri":"$request_uri",' '"status":$status,' '"host":"$host",' '"upstream":"$upstream_addr",' '"rt":$request_time}'; access_log /var/log/nginx/access_trace.log trace; 这里的 $remote_addr 是完成 realip 解析后的地址,$realip_remote_addr 是实际与源站建立 TCP 连接的上一跳。两者都记录,后续才能判断请求究竟来自哪个代理,以及转发链是否异常。 边缘反代处不要原样透传用户提交的同名头,应覆盖后再转发: proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Request-ID $request_id; proxy_set_header Host $host; 如果加速器使用专有头,例如 CF-Connecting-IP 或厂商自定义头,只能在确认连接来源属于厂商出口网段后读取。不要同时让应用在多个头之间“自动择优”,否则很容易被伪造。 应用层也要配置可信代理数量或网段,不能简单开启全局 trust proxy。以 Express 为例: app.set('trust proxy', (ip) => { return ip.startsWith('10.20.') || trustedCdnCidrs.contains(ip); }); 真实 IP 只适合辅助风控和审计,不建议作为后台权限、租户隔离或免登录依据。管理接口仍应使用 mTLS、独立鉴权或签名请求;否则代理配置一旦放宽,IP 白名单就会直接变成权限绕过点。 排查时可以分别构造直连和经代理请求,确认伪造头是否生效: # 若源站本应只允许代理访问,此请求应被防火墙拒绝 curl -vk \ -H 'Host: example.com' \ -H 'X-Forwarded-For: 127.0.0.1, 8.8.8.8' # 检查实际加载的 realip 和日志配置 nginx -T | grep -nE 'set_real_ip_from|real_ip_header|real_ip_recursive|log_format' # 对照连接层来源和解析后的客户端地址 tail -f /var/log/nginx/access_trace.log | jq . 最后建议在网络层只放行代理出口到源站的 80/443,并把加速器 IP 段更新做成可审计的定时任务。IP 段过期和临时手工放行,往往才是源站绕过与日志失真的实际原因。 创建帐户或登录后发表意见 注册帐户 立刻登录 分享 https://www.hackhat.org/Hacker-topic/27064.html/ 更多分享选项… 粉丝