跳转到帖子

咖啡馆实战:网站接入 CDN 后的真实 IP 信任边界与源站绕过排查

精选回复

发布于

网站接入 CDN、WAF 或 Nginx 反向代理后,应用看到的连接来源通常变成代理节点 IP。为了恢复访客地址,很多项目会直接读取 X-Forwarded-For,并将其用于后台访问控制、登录限速或审计。这类配置一旦没有限定可信代理,攻击者就可能伪造来源 IP;如果源站还能被直接访问,还会进一步绕过 CDN 上的防护策略。

下面以已授权的测试环境为例,说明如何验证代理头信任问题、确认源站暴露范围,并完成日志和配置修复。示例中的域名、地址均为测试占位符。

背景场景

测试站点的链路如下:

访问者 -> CDN/WAF -> Nginx 源站 -> Web 应用

公网域名:
源站域名:origin.example.test
源站地址:192.0.2.10
内部管理路径:/ops/

管理路径原计划只允许办公网访问,登录接口则按照客户端 IP 做限速。初步检查发现,应用直接使用 X-Forwarded-For 的第一个地址作为客户端 IP,而 Nginx 没有限制该头只能由 CDN 节点提供。

问题定位

先从正常入口建立基线,记录状态码、响应头和请求耗时。测试时不要只看页面内容,302、401、403、429 的变化通常更有判断价值。

curl -sk -D-  -o /dev/null
curl -sk -D-  -o /dev/null

然后在授权环境中分别提交常见代理头,观察管理路径或限速结果是否发生变化:

curl -sk -D-  \
  -H 'X-Forwarded-For: 127.0.0.1' \
  -o /dev/null

curl -sk -D-  \
  -H 'X-Real-IP: 10.10.20.15' \
  -o /dev/null

curl -sk -D-  \
  -H 'Forwarded: for=127.0.0.1' \
  -o /dev/null

如果不带头时返回 403,带入伪造地址后变成 200 或 302,说明访问控制依赖了可控请求头。即使页面仍要求登录,也应视为权限边界缺陷,因为 IP 白名单这一层已经失效。

对于登录限速,可以在专用测试账号和约定频率下验证:连续请求触发 429 后,仅修改 X-Forwarded-For 是否能恢复请求。不要在生产账号上做高频测试,避免锁号和污染业务指标。

检查源站是否可绕过 CDN

源站地址应由资产清单、云平台控制台或运维侧提供,不建议在未约定范围内做外部枚举。拿到测试源站地址后,可通过 curl 的 resolve 参数保持正确的 TLS SNI 和 Host:

curl -sk --resolve  \
  -D-  \
  -o /dev/null

若该请求能直接到达业务,并且响应中缺少 CDN 注入的请求标识,基本可以确认源站公网可达。此时还需要比较两条路径的安全策略:

  • CDN 路径是否有 WAF、Bot 管理、限速或地域规则。
  • 源站直连是否执行相同的认证和访问控制。
  • 源站证书是否接受公网域名,是否存在默认虚拟主机暴露。
  • 源站是否把任意直连请求误认为来自可信代理。

技术分析:问题通常出在三层

  • 网络层:安全组或防火墙允许任意公网地址访问源站 80、443 端口。
  • 代理层:Nginx 无条件接受 X-Forwarded-For、X-Real-IP 等客户端可控请求头。
  • 应用层:程序自行取 X-Forwarded-For 的第一个值,并据此执行白名单、限速或审计。

X-Forwarded-For 本身不是可信身份。它可能包含多个逗号分隔的地址,只有当请求确实来自受控代理,并且入口代理会覆盖或规范化该头时,才能作为客户端地址来源。尤其要避免“只要存在该头就信任”的实现。

Nginx 日志留痕

排查阶段建议同时记录 TCP 对端、realip 模块解析后的地址、原始代理头和请求标识。这样可以区分 CDN 节点、真实访客以及直接访问源站的请求。

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

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

proxy_set_header X-Request-ID $request_id;
proxy_set_header Host $host;

其中,启用 realip 模块后,remote_addr 是解析后的客户端地址,realip_remote_addr 保留原始连接对端。应用日志也应记录同一个 X-Request-ID,并附带认证主体、路由、结果和限速命中情况,避免只能看到 IP 却无法串联上下游请求。

可用以下命令快速筛选伪造头、源站直连和管理路径访问记录:

grep 'request="GET /ops/' /var/log/nginx/edge_access.log

grep 'xff="127.0.0.1' /var/log/nginx/edge_access.log

awk '$0 ~ /status=200/ && $0 ~ /\/ops\// {print}' \
  /var/log/nginx/edge_access.log

可信代理配置

如果使用 CDN 提供的专用客户端地址头,应只信任官方代理网段,并确认 CDN 会覆盖该头,而不是透传访客提交的同名头。以下地址仅为示例,实际配置必须使用服务商当前公布的完整网段:

# 仅示例:实际网段应由自动化任务同步并经过变更审核
set_real_ip_from 203.0.113.0/24;
set_real_ip_from 2001:db8:100::/48;

real_ip_header X-CDN-Client-IP;
real_ip_recursive on;

如果入口代理完全由自己控制,建议在公网入口覆盖外部传入的 X-Forwarded-For,而不是直接继承:

# 公网入口层:丢弃客户端伪造链,仅写入连接来源
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Request-ID $request_id;

在后续受控代理层才考虑追加地址。多层代理环境中,应明确每一跳由谁写入、是否覆盖、应用信任几跳,不能简单地取最左侧地址。

源站访问控制

最有效的修复是让源站只接受 CDN、负载均衡器或运维网络的连接。优先在云安全组或上游防火墙实施,因为请求在到达 Nginx 前就会被拒绝。下面的 nftables 片段仅展示规则意图,部署前应通过控制台保留管理通道并验证 IPv6 策略:

table inet origin_filter {
  set cdn_v4 {
    type ipv4_addr
    flags interval
    elements = { 203.0.113.0/24 }
  }

  chain input {
    type filter hook input priority 0
    policy drop

    ct state established,related accept
    iifname lo accept
    ip saddr @cdn_v4 tcp dport 443 accept
    ip saddr 10.20.0.0/16 tcp dport 22 accept
  }
}

不要直接复制到远程服务器执行。应先确认管理地址、健康检查地址、IPv6 入口和应急回滚方式,否则容易把运维连接一并阻断。对于支持回源认证的 CDN,还可以叠加双向 TLS 或回源签名,避免只依赖可能变化的 IP 网段。

权限边界修复

管理接口不应只依赖来源 IP。比较稳妥的组合是:

  • 网络层限制源站和管理入口的可达范围。
  • 代理层只接受受信节点写入的客户端地址。
  • 应用层使用独立身份认证和角色授权,IP 白名单只作为附加条件。
  • 限速同时绑定账号、设备或会话维度,避免仅靠 IP。
  • 后台高风险操作记录认证主体、请求标识、来源链和操作结果。

Nginx 侧可以保留网段限制,但不能替代应用认证:

location /ops/ {
    allow 10.20.0.0/16;
    deny all;

    auth_request /internal-auth;
    proxy_pass 
}

location = /internal-auth {
    internal;
    proxy_pass 
    proxy_pass_request_body off;
    proxy_set_header Content-Length '';
    proxy_set_header X-Original-URI $request_uri;
    proxy_set_header X-Request-ID $request_id;
}

修复后的复测

  • 从正常 CDN 路径访问,业务功能和真实客户端地址记录正常。
  • 提交伪造 X-Forwarded-For、X-Real-IP 后,权限和限速结果不发生变化。
  • 使用源站地址直连时,连接在安全组或防火墙层被拒绝。
  • 从非可信代理发送专用客户端地址头时,Nginx 不采用该值。
  • 管理接口同时校验来源范围、登录状态和角色权限。
  • Nginx、CDN 和应用日志能通过请求标识完成关联。

复测时需要特别关注配置更新后的网段同步机制。CDN 节点网段发生变化后,如果仍依赖人工维护,可能出现回源中断,或者为了恢复业务临时放开全部公网地址,导致问题重新出现。

总结

CDN 和反向代理接入后,客户端 IP 已经成为一条需要明确验证的信任链。真正需要确认的不是“日志里能否看到真实 IP”,而是连接来自谁、请求头由谁写入、应用信任哪一跳,以及源站是否允许绕过入口直接访问。修复应同时覆盖源站网络边界、代理头处理、应用授权和日志关联,单独修改某个 Header 的读取方式通常不够。

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

这类问题我一般先把“客户端真实 IP”和“源站访问入口”拆开排查,不要把 X-Forwarded-For 当成天然可信字段。真正的信任边界应该是:只有来自 CDN 官方回源网段的请求,才允许覆盖应用看到的客户端地址。

例如 Nginx 不建议直接写成:

proxy_set_header X-Real-IP $http_x_forwarded_for;

否则用户可以自己构造请求头,日志、限流和审计都会被污染。可以使用 CDN 公布的固定回源网段,并启用 realip 模块:

# 仅示例,实际网段必须替换为 CDN 官方公布的回源地址
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 main '$remote_addr - $realip_remote_addr '
                '"$request" "$http_x_forwarded_for"';

server {
    listen 443 ssl;
    server_name app.example.com;

    # 应用层使用 $remote_addr,只有可信 CDN 请求才会被 realip 重写
    location / {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass 
    }
}

如果前面还有 WAF、SLB 或多级代理,最好明确每一跳的来源和头部覆盖规则,避免把任意客户端传来的 XFF 链直接透传到应用。可以先在源站抓一小段访问日志,对比以下字段:

curl -sk  \
  -H 'Host: app.example.com' \
  -H 'X-Forwarded-For: 198.51.100.10'

sudo tcpdump -ni any 'tcp port 80 or tcp port 443'
sudo ss -lntp

测试重点不是看页面能否打开,而是确认:

  • 源站 80/443 是否只接受 CDN 回源网段,管理端口是否完全不暴露公网;
  • 直接访问源站 IP 时,SNI、Host 不匹配是否会被默认虚拟主机拒绝;
  • IPv4、IPv6 是否都做了相同的限制,实际排查中经常只封了 A 记录而漏掉 AAAA;
  • DNS 历史、旧解析、邮件/监控/测试域名是否暴露过同一源站地址;
  • 源站是否存在绕过 CDN 的上传、健康检查或回源专用域名。

源站侧最好再加一层网络访问控制,而不是只依赖 Host 校验。例如云安全组只放行 CDN 官方回源 CIDR;若必须保留直连管理,应使用独立管理入口、VPN 或零信任访问,不要和业务源站共用端口。

另外,如果 CDN 使用 PROXY protocol,就不要同时把它当作普通 HTTP 头解析。监听端和上游必须统一配置,否则可能出现连接解析异常,甚至把伪造的地址写入日志。最终验收建议用三组请求验证:正常 CDN 访问、直连源站 IP、伪造 XFF 的 CDN 访问,并分别检查应用日志、WAF 日志和源站 TCP 连接来源,确保三者的信任结果一致。

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

最近浏览 0

  • 没有会员查看此页面。