跳转到帖子

咖啡馆实战:网站接入 CDN 后的真实 IP 信任边界、源站绕过与日志修复

精选回复

发布于

背景

网站接入 CDN、WAF 或反向代理后,源站看到的连接地址通常是代理节点,而客户端真实 IP 由 X-Forwarded-For、X-Real-IP 等请求头传递。这里容易出现两个问题:一是源站无条件信任客户端可控请求头,导致访问控制、限流和审计被绕过;二是源站仍可被公网直接访问,使攻击者跳过 CDN 与 WAF。

下面以一套经过授权的测试环境为例:公网流量先到 CDN,再转发至 Nginx,后端应用根据客户端 IP 实施管理接口限制。重点检查代理信任边界、源站暴露、日志留痕以及修复后的验证方法。

所有探测和验证都应限定在自有系统或书面授权范围内。测试前应确认域名、源站地址、端口和时间窗口,避免影响第三方 CDN 节点。

问题定位

先画清请求链路,不要只检查应用代码。常见链路是“客户端—CDN/WAF—Nginx—应用”。需要分别确认连接层来源、代理传递的地址头,以及应用最终采用了哪个字段。

检查点需要确认的内容常见风险
CDN真实 IP 使用哪个请求头、节点出口网段不同厂商头名称不一致
Nginxreal_ip_header 与 set_real_ip_from信任任意来源提交的地址头
源站防火墙是否仅允许 CDN 回源网段公网可直接访问源站
应用限流和鉴权读取 REMOTE_ADDR 还是自定义头直接读取 X-Forwarded-For
日志是否同时记录连接地址、解析地址和转发链伪造后无法还原请求来源

授权验证步骤

第一步是在正常入口建立基线。使用测试域名请求一个无副作用的健康检查页面,同时保存响应头。部分 CDN 会返回节点标识或缓存命中信息,可辅助判断流量是否经过代理。

curl -skD normal.headers -o /dev/null \
  

sed -n '1,30p' normal.headers

第二步检查应用是否错误信任客户端传入的地址头。仅对测试接口发送一个保留地址作为标记,并在源站日志中核对最终解析结果。不要用管理员真实地址测试,避免误触发权限逻辑。

curl -skD forged.headers -o /dev/null \
  -H 'X-Forwarded-For: 192.0.2.123' \
  -H 'X-Real-IP: 192.0.2.123' \
   192.0.2.123 识别为真实来源,说明某一层直接采用了客户端可控字段。不过仅凭响应差异还不能下结论,需要结合 Nginx 日志确认是 CDN 追加了地址链,还是源站覆盖规则配置错误。

第三步验证源站是否能够被绕过。已知自有源站地址时,可以使用 curl 的 resolve 参数,将业务域名的 TLS SNI 与 Host 保持不变,但把连接定向到源站。这样比直接访问 IP 更接近真实回源请求。

ORIGIN_IP='203.0.113.10'

curl -skD origin.headers -o /dev/null \
  --resolve  \
  

sed -n '1,30p' origin.headers

若源站直接返回正常业务页面,而边界防火墙也没有限制来源,则 CDN/WAF 只是一条可选路径。此时攻击流量可以直接到达源站,绕过边缘限速、Bot 管理和部分规则。

技术分析:不要信任“请求头”,只信任“可信上一跳”

X-Forwarded-For 本质上是普通 HTTP 请求头。安全的处理方式不是简单读取最左侧或最右侧地址,而是先确认当前 TCP 连接是否来自可信代理,再按照确定的代理层数解析地址链。

下面这种配置风险较高,因为它允许任意来源通过 X-Forwarded-For 改写 Nginx 认定的客户端地址:

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

较合理的做法是仅信任 CDN 官方公布且经过变更管理的回源网段。以下地址仅为文档示例,部署时必须替换成实际网段:

# /etc/nginx/conf.d/realip.conf
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;

real_ip_recursive 开启后,Nginx 会从地址链末端向前排除可信代理,最终选择第一个非可信地址。其前提是可信代理网段配置准确;如果把整个互联网加入信任列表,递归解析反而会放大伪造风险。

日志留痕配置

排查这类问题时,只记录 remote_addr 往往不够。启用 realip 模块后,remote_addr 可能已经被替换,因此应同时保留原始连接地址、解析后的客户端地址和完整转发链。

log_format proxy_audit
  '$time_iso8601 request_id=$request_id '
  'peer=$realip_remote_addr client=$remote_addr '
  'xff="$http_x_forwarded_for" host="$host" '
  'request="$request" status=$status bytes=$body_bytes_sent '
  'rt=$request_time uaddr="$upstream_addr" '
  'urt="$upstream_response_time" ua="$http_user_agent"';

access_log /var/log/nginx/proxy_audit.log proxy_audit;

其中 peer 表示与 Nginx 建立连接的上一跳,client 表示经过可信代理规则解析后的地址。正常情况下,peer 应落在 CDN 回源网段;如果 peer 是普通公网地址且请求仍获得业务响应,通常意味着源站访问控制未收紧。

可以使用 awk 快速统计直接访问源站的连接来源。这里按日志中的 peer 字段提取,只用于初步聚类,正式调查应交叉核对防火墙、负载均衡和应用日志。

awk '{
  for (i=1; i<=NF; i++) {
    if ($i ~ /^peer=/) {
      sub(/^peer=/, "", $i)
      count[$i]++
    }
  }
}
END {
  for (ip in count) print count[ip], ip
}' /var/log/nginx/proxy_audit.log | sort -nr | head -30

源站访问控制

仅修正真实 IP 解析还不够。源站公网入口应在云安全组、主机防火墙或负载均衡访问控制层,仅放行 CDN 回源网段和必要的运维通道。应用层 allow 或 deny 可以作为补充,但不应替代网络层限制。

下面是 nftables 的结构示例。实施前应通过带外控制台确认回滚能力,并先验证 CDN 当前出口网段,避免误封生产回源流量。

table inet origin_filter {
  set cdn4 {
    type ipv4_addr
    flags interval
    elements = { 192.0.2.0/24 }
  }

  chain input {
    type filter hook input priority 0
    policy drop

    ct state established,related accept
    iifname "lo" accept

    # 运维入口应替换为实际堡垒机网段
    ip saddr 198.51.100.32/28 tcp dport 22 accept

    # 业务端口只允许 CDN 回源
    ip saddr @cdn4 tcp dport { 80, 443 } accept
  }
}

如果 CDN 支持回源双向 TLS、固定回源证书、签名请求头或私有链路,建议同时启用。固定请求头只能作为附加校验:如果源站仍公开可达且该值泄露,它不能替代网络层访问控制。

应用层权限边界

客户端 IP 适合做风控信号和辅助限流,不适合单独承担管理接口鉴权。即使真实 IP 解析完全正确,办公出口、代理节点和 NAT 环境也会让多个用户共享地址。管理接口至少应使用独立身份认证、细粒度授权和操作审计。

后端还应避免直接读取任意 X-Forwarded-For。更稳妥的方式是让 Nginx 完成可信代理解析,再把规范化后的地址传给应用,并覆盖外部同名头:

location / {
    proxy_set_header Host $host;
    proxy_set_header X-Client-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Request-ID $request_id;
    proxy_pass 
}

应用只消费网关写入的 X-Client-IP,同时确保应用端口不对公网或非受控内网开放。否则攻击者可以绕过 Nginx,直接连接应用并自行构造该请求头。

修复后的复测

  • 从正常域名访问,确认 CDN 回源成功,业务状态码和证书链正常。
  • 重复提交伪造 X-Forwarded-For,确认日志中的 client 不会被任意值覆盖。
  • 使用 resolve 直连源站,预期连接超时、被拒绝,或由独立默认站点返回固定拒绝响应。
  • 检查 peer 是否始终属于受信 CDN 网段,并对异常来源设置告警。
  • 确认应用限流、审计和管理接口读取的是规范化地址,而不是原始客户端请求头。
  • 模拟 CDN 网段更新流程,验证配置拉取、语法检查、灰度发布和回滚机制。

Nginx 配置发布前至少执行语法检查,并保留当前可用版本:

nginx -T > /var/backups/nginx-config-$(date +%F-%H%M%S).txt
nginx -t && systemctl reload nginx

# 发布后抽查审计日志
tail -n 50 /var/log/nginx/proxy_audit.log

风险与修复优先级

问题影响建议优先级
源站公网可直连绕过 WAF、限速与边缘访问控制高
信任任意 X-Forwarded-For伪造审计来源、绕过基于 IP 的策略高
应用端口可被直接访问绕过网关头部覆盖和统一鉴权高
日志只记录解析后地址事件发生后无法还原真实连接链路中
CDN 网段依靠人工维护网段变化导致中断或信任范围陈旧中

总结

网站加速链路的关键不是“有没有 CDN”,而是每一层是否建立了明确的信任边界。源站只接受可信代理连接,Nginx 只解析可信上一跳提供的地址链,应用只使用网关规范化后的字段,同时保留原始连接地址与转发链日志。完成这些配置后,再通过正常入口、伪造请求头和源站直连三组测试复核,通常能够把真实 IP 伪造、WAF 绕过和日志失真这几类问题一起收敛。

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

这类问题建议拆成三层处理:入口是否只能来自 CDN、转发头是否可信、应用与日志是否使用同一套客户端地址。

关键点不是“读取哪个 Header”,而是“只有 CDN 回源连接才有资格提供这个 Header”。如果源站仍可被公网直连,攻击者直接请求源站并伪造 X-Forwarded-For,限速、审计和封禁都会失真。

Nginx 可以按 CDN 官方公布的回源网段建立信任边界。假设 CDN 使用 X-Forwarded-For:

set_real_ip_from 203.0.113.0/24;
set_real_ip_from 2001:db8:1234::/48;

real_ip_header X-Forwarded-For;
real_ip_recursive on;

log_format edge '$time_iso8601 '
                'client=$remote_addr '
                'peer=$realip_remote_addr '
                'xff="$http_x_forwarded_for" '
                'host="$host" request="$request" '
                'status=$status bytes=$body_bytes_sent '
                'request_id="$http_x_request_id"';

access_log /var/log/nginx/access.log edge;

$remote_addr 是解析后的客户端地址,$realip_remote_addr 保留实际建立 TCP 连接的 CDN 节点地址。排障时两者都要记录,否则配置错误后很难还原。CDN 如果提供专用单值头,应优先使用其专用头,并确认该头会在边缘覆盖,而不是透传客户端同名字段。

源站侧最好直接做网络层收口,只允许 CDN 网段访问 80/443,运维入口另走固定地址或 VPN。例如使用 nftables:

table inet filter {
    set cdn4 {
        type ipv4_addr
        flags interval
        elements = { 203.0.113.0/24 }
    }

    chain input {
        type filter hook input priority 0;
        ct state established,related accept
        iif "lo" accept
        ip saddr @cdn4 tcp dport { 80, 443 } accept
        tcp dport { 80, 443 } drop
    }
}

还要检查常见的源站暴露路径:历史 DNS 记录、邮件头中的同机 IP、证书透明度记录、旧子域名、默认虚拟主机,以及同一 IP 上返回站点内容的其他端口。仅靠隐藏 DNS 不算防护。默认虚拟主机可以直接拒绝未知 Host,源站站点还可校验 CDN 回源携带的随机密钥头;不过密钥头只能作为补充,不能替代防火墙。

server {
    listen 443 ssl default_server;
    server_name _;
    return 444;
}

server {
    listen 443 ssl;
    server_name 

    if ($http_x_origin_verify != "一段足够长的随机值") {
        return 403;
    }

    # proxy_pass / fastcgi_pass ...
}

上线后建议做两组验证:一是直连源站并伪造转发头,确认被网络层或回源校验拒绝;二是经 CDN 请求,确认应用、访问日志和限速模块看到的地址一致。

# 直连源站测试,预期超时、拒绝或 403
curl -vk --resolve  \
  -H 'X-Forwarded-For: 1.2.3.4' \
  

# 经 CDN 测试
curl -sS -D-  -o /dev/null

# 检查配置与日志字段
nginx -T | grep -E 'set_real_ip_from|real_ip_header|real_ip_recursive|log_format'
tail -f /var/log/nginx/access.log

最后别手工长期维护 CDN 网段。应定时从官方地址拉取、校验格式,生成配置后先执行 nginx -t,成功再 reload;防火墙地址集也要同步更新。更新失败时保留上一版,避免一次空列表把源站全部开放或全部阻断。

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

最近浏览 0

  • 没有会员查看此页面。

SiteMap HACKHAT © 2026 All rights reserved.