跳转到帖子

网站上了 CDN,源站还在“裸奔”?一次 Nginx 回源边界与日志核查

精选回复

发布于

背景:加速生效,不等于源站受到保护

在一次授权的网站部署检查中,站点通过 CDN 对外提供服务,源站运行 Nginx,应用只监听本机端口。页面访问和证书都正常,但用域名绑定源站 IP 后,仍能直接访问业务。这意味着请求可以绕过 CDN 上的限速、访问规则和部分安全检查。

这类问题首先是网站建设和服务器配置问题,不需要扫描大量接口。本文用一个最小部署说明如何验证源站入口、收紧回源权限,并保留能定位问题的日志。所有地址均为文档示例地址,执行时只替换为自己管理或明确授权的资产。

  • 网站域名:site.example。
  • 源站地址:198.51.100.20。
  • 示例回源节点:192.0.2.10。
  • 应用监听:127.0.0.1:8080。

问题定位:先区分三个信任边界

  • 访问入口:源站是否只接受可信回源节点,而不是整个互联网?
  • 业务来源:请求是否确实来自自己的 CDN 配置,而不只是同一家 CDN 的某个共享节点?
  • 客户端身份:Nginx 是否只信任可信代理写入的客户端 IP 信息?

这三个问题不能只靠一条 IP 白名单解决。共享 CDN 的回源网段只能证明连接来自该服务商,不能单独证明请求属于本站;而客户端自己提交的 X-Forwarded-For,也不能直接当作真实来源。

验证方法:保持 Host 和 TLS SNI 一致

不要只访问 curl 的 --resolve,可以连接指定地址,同时保留正确的域名、Host 和 TLS SNI。

# 通过正常 DNS 访问,观察 CDN 链路
curl -sS -D - -o /dev/null 

# 直连授权源站,仍使用站点域名进行 TLS 和 HTTP 请求
curl --noproxy '*' --resolve site.example:443:198.51.100.20 \
  --connect-timeout 5 -sS -D - -o /dev/null \
  

# 检查应用端口是否只监听回环地址
ss -lntp | grep ':8080'

如果直连得到业务页面,说明这条入口确实可用。但返回 403、404 也不能直接算通过:要结合源站日志确认,是入口策略拒绝了请求,还是应用碰巧拒绝了当前路径。若源站使用 CDN 专用 CA 签发的证书,应使用对应的 --cacert 验证,不要把证书错误当成访问控制生效。

技术分析:恢复客户端 IP 后,连接来源可能被混淆

Nginx 启用 realip 模块后,$remote_addr 可以被替换为代理传入的客户端地址。因此,用它判断“连接是不是来自 CDN”容易出现逻辑错误。

  • $remote_addr:经过可信代理解析后,用于业务审计的客户端地址。
  • $realip_remote_addr:realip 模块保留的原始连接对端地址,适合检查直接连接源站的是谁。

另一个常见错误是配置 set_real_ip_from 0.0.0.0/0。这相当于允许任何连接者参与声明客户端地址,可能污染审计记录,以及依赖来源 IP 的访问控制或限流。

操作步骤:回源地址与站点凭据同时校验

优先在云安全组或主机防火墙上限制 443 端口,只允许实际回源网段。下面的 Nginx 配置作为第二层保护:同时检查原始连接对端、站点回源凭据和域名。CDN 需要在回源时覆盖写入 X-Origin-Verify,不能仅在浏览器没有提交该头时补充。

示例要求 Nginx 编译了 http_realip_module,可通过 nginx -V 查看。以下内容放在 http 上下文中;证书路径和占位凭据必须替换。192.0.2.10/32 只是示例,生产环境应使用服务商公布的实际回源网段,并建立更新机制。

# http 上下文
set_real_ip_from 192.0.2.10/32;
real_ip_header X-Forwarded-For;
real_ip_recursive on;

# 按原始 TCP 对端判断,不使用改写后的客户端地址
geo $realip_remote_addr $edge_peer_ok {
    default 0;
    192.0.2.10/32 1;
}

map $http_x_origin_verify $origin_key_ok {
    default 0;
    "REPLACE_WITH_RANDOM_ORIGIN_SECRET" 1;
}

map $host $origin_host_ok {
    default 0;
    site.example 1;
}

map "$edge_peer_ok:$origin_key_ok:$origin_host_ok" $origin_allowed {
    default 0;
    "1:1:1" 1;
}

log_format origin_audit escape=json
    '{"time":"$time_iso8601","rid":"$request_id",'
    '"peer":"$realip_remote_addr","client":"$remote_addr",'
    '"host":"$host","method":"$request_method","uri":"$uri",'
    '"status":$status,"allowed":$origin_allowed,'
    '"upstream":"$upstream_addr","up_status":"$upstream_status"}';

server {
    listen 443 ssl;
    server_name site.example;

    ssl_certificate /etc/nginx/tls/site.example.crt;
    ssl_certificate_key /etc/nginx/tls/site.example.key;

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

    # 此处 if 仅用于立即 return,不包含重写或代理逻辑
    if ($origin_allowed = 0) {
        return 403;
    }

    location / {
        proxy_pass 
        proxy_set_header Host site.example;
        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_set_header X-Origin-Verify "";
    }
}

生成凭据可以使用 openssl rand -hex 32。凭据只保存在 CDN 回源设置和受限的服务器配置中,不放进前端代码、仓库或工单截图。上例主动清除了发给应用的回源凭据,并重建转发头,避免应用继续解析未经约束的客户端输入。应用也应只信任本机 Nginx 作为代理。

这里的 X-Forwarded-For 配置有前提:CDN 必须按照明确且可靠的规则写入客户端地址。若服务商推荐使用专用客户端 IP 头,应按其文档调整 real_ip_header;不能直接套用一套头部规则到所有服务商。

上线与复测:不要只看首页能不能打开

# 检查配置,成功后再平滑加载
sudo nginx -t && sudo systemctl reload nginx

# 观察入口判定及是否进入上游
sudo tail -n 50 /var/log/nginx/origin_access.log

# 再次验证直连路径
curl --noproxy '*' --resolve site.example:443:198.51.100.20 \
  --connect-timeout 5 -sS -D - -o /dev/null \
   CDN 请求:确认产生了实际回源,而不是只命中边缘缓存;日志中 allowed 应为 1,peer 应为合法回源节点。
  • 公网直连源站:若网络层已限制,应无法建立连接;若仍能到达 Nginx,应返回 403,且 upstream 为空或为“-”。
  • 从自有测试机伪造 X-Forwarded-For:不应改变其非可信连接身份,更不能因此访问业务。
  • 在自有回源测试环境中移除凭据:即使连接来自允许的节点,也应被拒绝。
  • 检查 IPv6、其他监听端口及同机虚拟主机,避免只封住 IPv4 的 443,应用端口却仍然公开。
  • 测试正常回源时,可以使用站点已有的不缓存健康检查路径,并确认日志确实新增记录。不要为了验证方便,临时开放一个不受回源控制的健康检查接口。

    风险与修复建议

    • 共享回源网段不是站点身份认证。条件允许时,使用站点专属回源客户端证书或私有网络回源;固定头部凭据属于共享秘密方案,需要轮换。
    • 日志不要记录回源凭据、Cookie、Authorization。示例记录 $uri 而非 $request_uri,避免默认收集查询参数中的令牌,但路径本身仍可能含敏感信息。
    • 来源头解析规则变化会影响限流、封禁和审计。调整 realip 配置后,应同时核对应用看到的客户端地址。
    • 回源网段过期可能造成全站故障。更新列表时先校验、再加载,并保留回滚配置;不要以临时放开全部来源作为长期修复。
    • 如果采用证书自动续期,提前确认验证方式是否依赖公网直连源站;必要时改用 DNS 验证,避免上线后才发现续期失败。

    总结

    网站接入 CDN 后,值得核查的不是“有没有加速响应头”,而是直连源站是否仍能进入业务、客户端身份是否可信、拒绝动作是否留下可解释的日志。把网络入口、站点回源认证和代理头信任分开处理,配置才更容易验证,也更容易在故障时定位责任边界。

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

    这里建议重点核查一个容易漏掉的点:Nginx 启用 Real IP 后,访问控制用的究竟是客户端地址,还是实际回源连接地址。

    如果配置了 set_real_ip_from 和 real_ip_header,$remote_addr 可能已经被替换成访客 IP。这时直接在 Nginx 里用 allow CDN网段; deny all;,访问控制判断的也是替换后的地址,可能误拦正常回源。CDN 网段白名单优先放到安全组或主机防火墙;必须在 Nginx 做时,应基于 $realip_remote_addr 判断连接对端,而不是直接照搬这套 allow/deny。

    日志建议同时保留还原前后的地址,避免只看“客户端 IP”就误以为流量经过了 CDN。下面配置放在 http 块中,相关变量需要 Real IP 模块支持:

    log_format origin_check escape=json
        '{"time":"$time_iso8601",'
        '"peer":"$realip_remote_addr",'
        '"client":"$remote_addr",'
        '"host":"$host",'
        '"sni":"$ssl_server_name",'
        '"xff":"$http_x_forwarded_for",'
        '"request":"$request",'
        '"status":$status}';
    
    access_log /var/log/nginx/origin_check.log origin_check;

    peer 用于核对连接来源是否属于 CDN 官方回源网段;client 用于业务溯源。XFF 只作为核查字段,不能直接当作可信身份,尤其不要配置 set_real_ip_from 0.0.0.0/0;。另外,源站四层拦截的请求不会进入 Nginx 日志,要结合安全组流日志或防火墙计数确认。

    验证时不要只访问“源站 IP”,还要带上正确的 Host 和 TLS SNI,否则可能只是被默认虚拟主机挡住:

    # 从非 CDN 的外部网络测试,替换为自己的业务域名和源站地址
    curl --noproxy '*' -v --connect-timeout 5 \
      --resolve  \
      
    
    

    预期应是连接被阻断,或命中明确的拒绝策略,而不是返回正常业务内容;返回跳转也要继续检查是否进入了业务虚拟主机。最后别漏掉 IPv6、80 端口和备用监听端口。若还需要限制“只有自己的 CDN 配置才能回源”,仅放行共享 CDN 网段不够,优先使用 CDN 支持的回源 mTLS 等身份认证能力。

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

    最近浏览 0

    • 没有会员查看此页面。

    SiteMap HACKHAT © 2026 All rights reserved.