跳转到帖子

网站接了 CDN,源站就安全了吗?一次服务器入口边界与 Nginx 日志排查

精选回复

发布于

背景:加速生效,不代表源站已经收口

网站建设里很常见的一种部署是:域名接入 CDN,回源到一台运行 Nginx 的服务器,Nginx 再转发给本机应用。页面访问速度正常,证书也没有报错,于是大家默认公网流量已经全部经过 CDN。

但在授权检查中,需要单独验证一个问题:知道源站地址后,是否还能绕过 CDN,直接访问相同站点?如果可以,CDN 上配置的限速、访问控制和部分防护规则,就不再是这个网站唯一的入口边界。

本文只讨论自有或明确授权的网站服务器。示例域名为 198.51.100.20,地址均为文档示例,操作时需替换为自己的测试资产。

问题定位:先区分访问路径,再看响应状态

不要只检查域名是否解析到了 CDN。DNS 只能说明正常解析路径,不能证明源站拒绝了其他路径。建议先对照三类请求:

  • 正常通过 CDN 访问站点,确认业务可用。
  • 保持相同域名和 TLS SNI,直接连接源站,确认入口是否受限。
  • 直接连接源站,同时伪造客户端地址头,检查是否错误地把请求头当成访问凭据。

测试路径可以使用站点已有的轻量健康检查接口。避免选取下单、发信等有业务副作用的接口。

# 1. 正常访问:经过域名当前解析路径
curl --noproxy '*' -sS -o /dev/null -D - \
  

# 2. 固定连接源站,但仍使用正确的 Host 和 TLS SNI
curl --noproxy '*' --connect-timeout 5 \
  --resolve  \
  -sS -o /dev/null -D - 

# 3. 检查伪造来源头是否影响源站准入
curl --noproxy '*' --connect-timeout 5 \
  --resolve  \
  -H 'X-Forwarded-For: 192.0.2.10' \
  -sS -o /dev/null -D -   -k。前者可能落到错误的虚拟主机,后者会掩盖证书问题。使用 --resolve 可以在不修改公共 DNS 的情况下,保留正常的域名校验和站点选择。

如果源站使用仅供 CDN 校验的源站证书,应通过 --cacert 指定相应可信 CA。证书校验失败本身不能证明入口已封闭。

技术分析:连接来源和访客地址是两回事

接入 CDN 后,源站看到的 TCP 连接对端通常是 CDN 节点。访客地址则通过约定的 HTTP 头传递。前者用于判断“谁可以连接源站”,后者主要用于业务日志和访客维度的限流。

常见配置错误是把 set_real_ip_from 写成 0.0.0.0/0,允许任意连接方提供可信客户端地址。另一个容易忽略的问题是:启用 realip 模块后,$remote_addr 可能已经变成还原后的访客地址,不能再不加区分地用它判断连接是否来自 CDN。

Nginx 的 $realip_remote_addr 保留 realip 改写前的地址,可用于核对直接连接来源。下面给出一个应用层兜底配置;网络层仍应优先限制入口。

操作步骤:收紧信任来源,并保留两层地址

先确认 Nginx 带有 realip 模块:

nginx -V 2>&1 | grep -- --with-http_realip_module

以下内容放在 http 上下文。192.0.2.0/24 只是配置格式示例,必须替换为 CDN 官方发布的真实回源网段;如果 CDN 提供更小范围的专用回源地址,应优先采用。使用 X-Forwarded-For 前,也要确认服务商的覆盖、追加与清洗规则。

# http 上下文
set_real_ip_from 192.0.2.0/24;
real_ip_header X-Forwarded-For;
real_ip_recursive on;

# 判断改写前的连接对端,而不是还原后的访客地址
geo $realip_remote_addr $origin_peer_allowed {
    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",'
    '"host":"$host",'
    '"method":"$request_method",'
    '"uri":"$uri",'
    '"status":$status,'
    '"origin_peer_allowed":$origin_peer_allowed,'
    '"request_time":$request_time}';

在已有 HTTPS 站点的 server 块中加入以下内容,保留原来的证书和业务配置。此处 if 只执行 return,不涉及复杂重写。

# 加入目标站点已有的 server 块
access_log /var/log/nginx/origin_audit.log origin_audit;

if ($origin_peer_allowed = 0) {
    return 403;
}

# 原有 location /、proxy_pass 等业务配置保持不变

部署前,先核对健康检查、证书续期、监控探测是否也经过 CDN。确有直连需求时,为其设计独立入口和明确的来源限制,不要为了让探测变绿而重新允许所有地址。

# 先检查语法,再平滑加载
sudo nginx -t && sudo systemctl reload nginx

# 查看被应用层入口规则拦截的请求
sudo tail -n 100 /var/log/nginx/origin_audit.log \
  | jq -c 'select(.origin_peer_allowed == 0) \
    | {time, peer, client, host, uri, status}'

验证结果:不能只看一个 403

  • 正常 CDN 请求应保持可用,日志中的 peer 应属于预期回源网段,client 应符合服务商的真实地址传递规则。
  • 非白名单地址直连源站,应在网络层被拒绝,或被 Nginx 返回 403,不能返回正常业务内容。
  • 伪造 X-Forwarded-For 后,直连请求仍应失败,不能因为头部内容变化获得准入。
  • 对照时间、路径和源站日志确认拦截位置。CDN 返回的 403、应用自身返回的 403,都不能直接证明源站入口规则生效。

如果已经在云安全组或防火墙阻断直连,测试请求不会进入 Nginx,此时没有访问日志是正常现象。应结合安全组命中记录、流日志或短时受控抓包核验,不要为了得到一条 Nginx 日志而放开入口。

风险与修复建议

  • 优先在安全组或防火墙中限制源站 80/443 的连接来源,IPv4 和 IPv6 都要检查。只收紧 IPv4,而 IPv6 仍开放,是常见遗漏。
  • 检查应用端口是否仍对公网监听,例如 8080、8000。即便 Nginx 已收口,直接暴露的后端仍可能成为另一条入口。
  • 逐一核对同一地址上的默认站点、其他 server_name 和其他监听端口。只修改一个业务 server 块,不等于整台服务器已经收口。
  • CDN 网段白名单只能证明请求来自服务商网络,不能必然证明来自自己的 CDN 配置。服务商支持时,应增加回源 mTLS 等身份校验,并确认凭据的隔离范围。
  • 回源网段需要维护。自动更新时,应校验下载结果非空、变更范围合理,并保留上一版配置;测试通过后再加载,避免错误更新造成全站中断。
  • 日志不要默认采集 Cookie、Authorization 或完整查询串。即便只记录 URI,路径中也可能含有敏感标识,仍需设置访问权限和合理保留周期。

总结

给网站接入 CDN,解决的是正常访问路径上的加速与防护问题;限制源站入口,解决的是其他路径能否绕过去的问题。这两件事需要分别验证。

实际落地时,至少保留三项可复查证据:正常 CDN 访问成功、非授权直连失败、连接来源与访客地址能够在日志中区分。做到这一步,后续讨论限速、防护规则和异常流量,才有可靠的入口边界作为基础。

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

接了 CDN 只代表正常流量经过 CDN,不等于源站入口已经收口。排查时建议把“网络边界”和“Nginx 对真实来源的识别”分开看,否则日志里很容易把客户端 IP、CDN 节点 IP 混在一起。

先确认源站实际暴露面:

ss -lntup
nft list ruleset
# 使用 iptables 的机器
iptables -S
ip6tables -S

# 同时检查云平台安全组、IPv6 入站规则
curl -4 ifconfig.me
curl -6 ifconfig.me

常见遗漏是只限制了 IPv4 的 80/443,但 IPv6、8080、8443 或面板端口仍可直连。云安全组和系统防火墙也要同时检查,不能只改其中一层。

已知源站 IP 时,可以从外部验证是否能绕过 CDN:

curl -vk --resolve example.com:443:源站IP 
curl -v -H 'Host: example.com' 

如果能拿到正常站点响应,说明源站仍接受公网直连。只靠 Nginx 的 server_name 或默认站点拦截不够,因为请求者可以自行设置 Host/SNI。更可靠的做法是:

  1. 安全组或防火墙仅允许 CDN 官方回源网段访问 80/443;
  2. SSH、管理端口只允许固定运维 IP 或内网/VPN;
  3. 条件允许时启用 CDN 的回源 mTLS(Authenticated Origin Pull);
  4. CDN 网段需要自动同步,避免网段变更后误封回源。

Nginx 日志建议同时记录连接对端和还原后的客户端地址:

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

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

配置 real IP 时,必须只信任 CDN 回源网段,不能直接对全网信任 X-Forwarded-For:

# 以下网段仅为格式示例,应替换为 CDN 官方公布的回源地址
set_real_ip_from 192.0.2.0/24;
set_real_ip_from 2001:db8:1234::/48;

# 按 CDN 实际提供的头选择,例如 CF-Connecting-IP、True-Client-IP
real_ip_header CF-Connecting-IP;
real_ip_recursive on;

如果写成 set_real_ip_from 0.0.0.0/0,直连源站的人可以伪造请求头污染日志,基于 IP 的限速、封禁和后台白名单也可能被绕过。

日志排查可以先看连接对端是否都是 CDN 节点,以及异常 URI、状态码和请求耗时:

# 统计连接对端
awk '{
  for (i=1;i<=NF;i++)
    if ($i ~ /^peer=/) {sub(/^peer=/,"",$i); print $i}
}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head -30

# 常见扫描路径
grep -E '(\.\./|/\.env|/wp-login|/phpmyadmin|/actuator|/vendor/phpunit)' \
  /var/log/nginx/access.log | tail -100

# 汇总状态码
awk '{
  for (i=1;i<=NF;i++)
    if ($i ~ /^status=/) {sub(/^status=/,"",$i); print $i}
}' /var/log/nginx/access.log | sort | uniq -c | sort -nr

如果日志中的 peer 出现非 CDN 地址,基本可以确认存在直连;如果全部是 CDN 节点,但攻击请求仍很多,则应继续检查 CDN 是否只是代理而没有启用 WAF、速率限制,或者缓存规则是否导致动态接口直接回源。

另外还要检查历史 DNS、旧的 A/AAAA 记录、证书透明度记录、邮件头和其他子域是否泄露源站 IP。更换源站 IP 只能作为泄露后的补救,最终还是要靠安全组/CDN 回源认证把入口真正收紧。

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

最近浏览 0

  • 没有会员查看此页面。

SiteMap HACKHAT © 2026 All rights reserved.