跳转到帖子

网站接入 CDN 后,如何验证源站访问边界与 Nginx 真实 IP 配置

精选回复

发布于

背景:加速生效了,源站却仍能被直接访问

网站接入 CDN 后,常见的遗漏不是缓存规则,而是源站边界:业务域名已经走加速节点,源站公网 443 端口却仍对所有地址开放。知道源站地址的人可以携带正确的域名直接访问,绕过部署在 CDN 上的限速、访问控制或 WAF。

另一个容易同时出现的问题是:为了恢复访客 IP,Nginx 无条件信任 X-Forwarded-For,导致攻击者自行填写的地址进入访问日志,甚至影响基于 IP 的后台访问控制。

下面以“CDN → Nginx → 本机应用”为例,只讨论自有或明确授权的网站环境,目标是验证加速入口、源站权限边界和日志是否一致。

一、先确定三种地址各自代表什么

  • 连接对端地址:直接与源站建立 TCP 连接的地址,正常情况下应该是 CDN 回源节点。
  • 访客地址:由可信 CDN 通过约定请求头传递,经过 realip 模块校验后使用。
  • 原始请求头:客户端可能参与构造的数据,不能直接作为身份或访问权限依据。

Nginx 启用 realip 模块后,$remote_addr 可能变成解析出的访客地址;$realip_remote_addr 则保留替换前的连接对端。限制“谁能连接源站”时,应检查后者,不能把访客地址误当成回源节点地址。

二、问题定位:用四组请求区分边界

准备一个由自己控制、不缓存的健康检查路径,例如 /__origin_probe,仅返回 204 或固定文本,不输出环境变量、版本信息和内部地址。测试时同时观察 CDN 日志与源站日志,避免把缓存命中误认为源站已收到请求。

# 1. 正常通过 CDN 访问
curl -sS -D - -o /dev/null 

# 2. 指定连接源站,但保留正确的 Host 与 TLS SNI
curl -sS -D - -o /dev/null \
  --resolve site.example:443:203.0.113.20 \
  

# 3. 直连源站,同时伪造转发地址
curl -sS -D - -o /dev/null \
  --resolve site.example:443:203.0.113.20 \
  -H 'X-Forwarded-For: 198.51.100.77' \
  

# 4. 通过 CDN 携带相同的伪造头,检查边缘如何处理
curl -sS -D - -o /dev/null \
  -H 'X-Forwarded-For: 198.51.100.77' \
   IP 均为文档示例,执行前替换为授权目标。不要为了让测试“通过”而加 -k 忽略证书校验;TLS 配置错误也应单独记录。

预期结果:正常 CDN 请求成功;直连源站被网络层拒绝,或被 Nginx 返回 403;伪造请求头不能改变这一结果。第四组需要核对日志:记录的访客地址应是实际测试出口,而不是手工填写的地址。

三、Nginx 配置:分开处理访客 IP 与回源权限

以下片段放在 http 上下文及对应业务 server 中。示例网段不代表任何实际 CDN,必须替换为服务商公布且适用于该产品的回源网段;IPv4、IPv6 都要覆盖。

# http 上下文:只信任明确的代理网段
set_real_ip_from 192.0.2.0/24;
real_ip_header X-Forwarded-For;
real_ip_recursive on;

# 使用原始连接对端判断是否来自允许的回源节点
geo $realip_remote_addr $allowed_origin_peer {
    default 0;
    192.0.2.0/24 1;
}

log_format origin_audit escape=json
    '{"time":"$time_iso8601",'
    '"request_id":"$request_id",'
    '"peer":"$realip_remote_addr",'
    '"client":"$remote_addr",'
    '"xff":"$http_x_forwarded_for",'
    '"host":"$host",'
    '"method":"$request_method",'
    '"uri":"$uri",'
    '"status":$status,'
    '"upstream_status":"$upstream_status"}';

server {
    listen 443 ssl;
    server_name site.example;

    ssl_certificate /etc/nginx/tls/site.example/fullchain.pem;
    ssl_certificate_key /etc/nginx/tls/site.example/privkey.pem;

    access_log /var/log/nginx/origin_audit.log origin_audit;

    # 此处仅使用 if + return,不做复杂重写
    if ($allowed_origin_peer = 0) { return 403; }

    location = /__origin_probe {
        return 204;
    }

    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-Request-ID $request_id;
        proxy_pass 
    }
}

real_ip_recursive on 会按可信代理关系解析地址链,但它不是请求头真实性校验器。必须确认 CDN 会覆盖用户提交的同名头,或在地址链末端追加实际连接者地址;如果服务商提供专用的单值访客 IP 头,应按其官方约定调整。不要直接照搬另一家 CDN 的头名称和解析规则。

这份配置适用于 CDN 直接连接 Nginx 的拓扑。如果中间还有负载均衡器,源站看到的连接对端可能是内网 LB。此时要重新设计各跳的可信关系,不能简单把整个内网都加入信任列表。

四、上线顺序与日志验收

# 确认构建包含 realip 模块
nginx -V 2>&1 | grep -- --with-http_realip_module

# 检查最终配置;输出可能包含敏感配置,不要直接贴到公开论坛
sudo nginx -T

# 语法校验通过后平滑重载
sudo nginx -t && sudo systemctl reload nginx

# 确认应用未直接暴露到公网
sudo ss -lntp | grep ':8080'

# 检查探测请求在源站的记录
sudo tail -n 100 /var/log/nginx/origin_audit.log

应用监听地址应为 127.0.0.1:8080、受限的内网地址或 Unix socket,而不是无意开放的 0.0.0.0:8080。使用容器时还要核对端口发布规则,不能只检查容器内部监听。

  • 正常回源:peer 属于 CDN 网段,client 是真实测试出口,状态码为 204。
  • 普通直连:peer 是测试出口,client 不因伪造头改变,状态码为 403。
  • 网络层已拦截:直连没有 Nginx 日志是合理现象,应结合安全组或防火墙计数验证。
  • 应用请求:用 Nginx 生成的 request_id 关联应用日志,不把客户端自行提交的追踪标识直接当成可信审计标识。

示例日志使用 $uri,避免默认记录查询参数中的令牌,但 URL 路径和请求头本身仍可能包含敏感数据。原始 XFF 建议只在排查期按需保留,并设置日志权限、轮转与保留期限。

五、风险与修复建议

  • set_real_ip_from 0.0.0.0/0 等于让任意来源声明访客地址,应删除;IPv6 的全网信任同理。
  • 优先在安全组或防火墙限制回源来源,Nginx 校验作为补充。只做 HTTP 拒绝仍会消耗 TLS 握手和连接资源。
  • CDN 网段是共享基础设施,不是某个站点的独有身份。对更严格的源站保护,应使用服务商支持的、绑定本账户或本服务的回源 mTLS,或受认证保护的私有回源通道。
  • 回源网段更新应经过格式校验、差异审查、配置测试和回滚准备,不要把下载结果未经校验直接加载进生产配置。
  • Host 限制和默认虚拟主机只能处理错误域名请求,不能阻止携带正确 Host、SNI 的直连。
  • 管理后台仍应使用独立认证、MFA 或受控管理网络。真实 IP 解析正确,并不意味着可以只靠 IP 完成高权限认证。

总结

网站接入加速服务后,验收不能只看域名是否解析到 CDN。还要分别验证:源站是否拒绝非授权连接、访客地址是否来自可信解析链、应用是否存在旁路端口,以及日志能否区分连接对端与最终访客。把这几项写进上线检查表,比事后仅凭一条 X-Forwarded-For 追查可靠得多。

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

建议把“源站边界”和“真实 IP”分开验证,前者看 TCP 对端是否只能来自 CDN,后者看 Nginx 是否只信任这些对端提供的头。

1. 先从 CDN 外部节点直连源站

不要只用源站 IP 访问,HTTPS 下还要带正确的 Host 和 SNI:

curl -vk --connect-timeout 5 \
  --resolve  \
  '

正确结果应是超时、拒绝连接或被防火墙丢弃,而不是返回站点内容。IPv4、IPv6 都要测,很多场景只封了 A 记录对应地址,AAAA 仍能直连。

源站限制最好放在安全组或主机防火墙,只允许 CDN 官方出口网段访问 80/443。不要单纯依赖 Nginx 的 allow/deny,因为启用 realip 后 $remote_addr 已经可能被改写成访客 IP,容易误判。证书签发、监控探针、回源健康检查等例外地址需要单独列入白名单。

2. Nginx 只信任 CDN 网段,不要全局信任请求头

以 CDN 提供 X-Forwarded-For 为例:

http {
    # 必须替换成 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;

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

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

这里:

  • $remote_addr 是 realip 模块处理后的访客地址;
  • $realip_remote_addr 是实际与 Nginx 建立连接的对端,正常应落在 CDN 网段;
  • set_real_ip_from 0.0.0.0/0 或信任整个内网大网段都不合适,否则客户端可伪造头部。

如果厂商提供的是经过清洗的专用头,例如 CF-Connecting-IP,优先按厂商文档使用该头:

real_ip_header CF-Connecting-IP;

如果 CDN 使用 PROXY Protocol,则应配置为:

listen 443 ssl proxy_protocol;
real_ip_header proxy_protocol;

这两种模式不要凭猜测混用;CDN 未发送 PROXY Protocol 时开启它,会导致正常回源直接失败。

3. 做一次伪造头验证

curl -sS \
  -H 'X-Forwarded-For: 1.2.3.4' \
  '

随后检查日志:

tail -n 20 /var/log/nginx/access.log
nginx -T | grep -E 'set_real_ip_from|real_ip_header|real_ip_recursive'

日志中的 client 不应变成伪造的 1.2.3.4;peer 应是 CDN 回源地址。如果前面还有 SLB、Ingress 或本地反向代理,需要把完整代理链梳理清楚,并确保每一跳都覆盖/追加头部且只信任上一跳。

最后建议持续监控 $realip_remote_addr:一旦出现非 CDN 网段,通常意味着防火墙漏放、IPv6 未限制,或者存在另一条可直达源站的入口。

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

最近浏览 0

  • 没有会员查看此页面。

SiteMap HACKHAT © 2026 All rights reserved.