发布于10月9日10月9日 背景最近帮一个中小型站点做授权安全检查,站点架构比较常见:域名接入 CDN,源站是一台云服务器,Nginx 反代 PHP-FPM,后台管理路径做了基础访问控制。客户的直观理解是“上了 CDN,源站 IP 就安全了”。但实际测试中发现,源站仍然可以被直接访问,并且绕过了 CDN 层的部分访问控制规则。这个话题放在咖啡馆板块比较合适:它不是单纯漏洞复现,也不是工具使用,而是网站建设和服务器运维中很常见的安全边界问题。下面按一次真实排查流程展开,重点放在定位方法、配置检查、日志留痕和修复建议。问题定位授权范围内,先确认业务访问链路:公网用户访问: CNAME 到 CDN 厂商节点CDN 回源到云服务器公网 IP源站 Nginx 根据 Host 分发到业务站点初步检查发现两个异常点:直接访问源站公网 IP 时,仍然返回了业务首页。使用源站 IP 加 Host 头访问,可以完整访问网站,且响应头中看不到 CDN 注入的特征头。这意味着攻击者一旦拿到源站 IP,就可以绕过 CDN 的 WAF、CC 防护、访问频控以及部分地域策略,直接打源站。技术分析源站暴露通常不是单点问题,常见原因有几类:历史 DNS 记录泄露,例如旧 A 记录、测试子域名、邮件记录。源站服务监听 0.0.0.0,且安全组允许全网访问 80/443。Nginx 默认站点未限制,IP 访问也落到了业务 server 块。回源鉴权缺失,源站只认 Host,不校验 CDN 来源 IP 或自定义回源头。日志中没有区分 CDN 访问和直连访问,导致长期未发现。这次的核心问题是:云安全组放行了 0.0.0.0/0 的 80、443,同时 Nginx 的 default_server 配置直接指向了业务站点。只要知道源站 IP,再带上正确 Host,就能绕过 CDN。操作步骤以下命令仅用于授权测试环境。先从客户端确认域名解析和访问链路:dig +short curl -I # 测试源站 IP 是否可直接访问 curl -I # 带 Host 头访问源站,确认是否命中业务站点 curl -k -I -H 'Host: 200、301、302,并且响应内容或 Location 指向真实业务路径,就需要继续看源站日志。Nginx 默认日志如果只记录 remote_addr,在接入 CDN 后容易误判。建议临时增加一个更清晰的日志格式,把 Host、X-Forwarded-For、CDN 特征头都打出来。log_format cdn_check '$remote_addr - $host [$time_local] ' '"$request" $status $body_bytes_sent ' 'xff="$http_x_forwarded_for" ' 'realip="$http_x_real_ip" ' 'ua="$http_user_agent" ' 'cdn_ray="$http_cf_ray" ' 'via="$http_via"'; access_log /var/log/nginx/access_cdn_check.log cdn_check;重新加载配置后观察直连请求和 CDN 请求的差异:nginx -t && systemctl reload nginx tail -f /var/log/nginx/access_cdn_check.log如果看到 remote_addr 是非 CDN 节点 IP,并且 Host 为业务域名,这类请求基本可以判断为直连源站。进一步可以统计最近一段时间是否已经存在绕过 CDN 的访问:awk '{print $1}' /var/log/nginx/access_cdn_check.log | sort | uniq -c | sort -nr | head grep ' /var/log/nginx/access_cdn_check.log \ | grep -v 'cdn厂商IP段关键字或已知节点' \ | tail -n 50实际环境里不建议用 grep 简单排除 CDN IP 段做长期判断,更稳妥的是把 CDN 官方 IP 段维护成 allowlist,并在防火墙或 Nginx 层做限制。关键配置示例第一步,修正 Nginx 默认站点。不要让 IP 访问落到业务站点,可以直接返回 444 或固定的 403。server { listen 80 default_server; listen 443 ssl default_server; server_name _; ssl_certificate /etc/nginx/ssl/default.crt; ssl_certificate_key /etc/nginx/ssl/default.key; return 444; }第二步,在业务 server 块中限制来源。更推荐在云安全组或系统防火墙层面只允许 CDN 回源 IP 访问 80/443。Nginx 层可以作为补充,但不要把它当唯一边界。# 示例:仅演示结构,实际 IP 段应以 CDN 厂商官方公布为准 geo $cdn_allowed { default 0; 203.0.113.0/24 1; 198.51.100.0/24 1; } server { listen 80; server_name if ($cdn_allowed = 0) { return 403; } location / { proxy_pass proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }第三步,如果 CDN 支持自定义回源 Header,可以增加一层轻量校验。注意这个 Header 不能暴露在前端响应中,也不要写得过于简单。server { listen 80; server_name if ($http_x_origin_verify != "change-this-to-a-long-random-value") { return 403; } location / { proxy_pass } }这类 Header 校验不能替代 IP allowlist,因为 Header 可以伪造;但在“CDN IP 段更新滞后”或“多 CDN 回源”的场景下,它能提供额外防护。日志留痕与排查重点加固后建议至少观察 24 到 72 小时,重点看以下几类日志:Nginx access 日志中是否仍有非 CDN IP 访问业务 Host。Nginx error 日志中是否出现大量 403、444,判断是否有人在探测源站。云防火墙或安全组日志中是否有针对 80/443 的直连扫描。应用日志中是否存在异常后台路径访问、批量参数探测、异常 UA。可以用一条简单命令查看被拦截的来源分布:grep ' 403 ' /var/log/nginx/access_cdn_check.log \ | awk '{print $1}' \ | sort | uniq -c | sort -nr | head -20如果在加固前已经发生过直连访问,建议回溯这些 IP 的请求路径。尤其关注:/admin、/manage、/login 等后台入口。/.env、/config.php、/backup、/sql、/zip 等敏感文件探测。带有明显注入、文件包含、路径穿越特征的参数。高频 POST 请求或异常 500 响应。风险与修复建议问题风险建议源站 IP 可直接访问绕过 CDN 防护,直接消耗源站资源安全组仅允许 CDN 回源 IP,默认站点 return 444Host 头即可命中业务攻击者可通过源站 IP 访问完整业务业务 server 限制来源,并校验回源 Header日志未区分真实来源难以及时发现直连和绕过行为记录 XFF、Host、CDN 特征头,定期统计异常来源历史解析记录泄露源站 IP 被动暴露清理旧 A 记录、测试域名,源站换 IP 后同步加固个人经验:很多站点并不是被“打穿 CDN”,而是源站一直裸露在公网。CDN 只能保护经过它的流量,不能自动保护错误开放的源站端口。总结网站接入 CDN 后,真正要确认的是“用户是否只能通过 CDN 到达源站”。排查时不要只看 DNS 是否 CNAME 到 CDN,还要验证源站 IP、Host 头、默认站点、安全组和访问日志。比较稳的加固顺序是:先收敛云安全组,再修正 Nginx 默认站点,然后增加回源 Header 校验,最后通过日志持续观察。这类问题不复杂,但很容易被忽略。对中小站点来说,把源站访问边界处理好,往往比堆很多上层安全策略更实际。 网络请求、日志与边界流量分析示意
10月9日10月9日 这类排查建议把“发现源站”和“阻断直连”分开做,很多站接了 CDN 但源站 80/443 仍然全网可达,后面再怎么隐藏 DNS 都不稳。 1)先确认是否存在直连源站特征 如果你已经怀疑某个 IP 是源站,可以用 Host 头直接验证,不要只看 IP 默认页: curl -I --resolve example.com:443:1.2.3.4 curl -I -H 'Host: example.com' 重点看这几类差异: Server、X-Powered-By、自定义响应头是否和 CDN 后一致; 错误页样式是否是源站 Nginx/Apache 默认页; TLS 证书是否包含业务域名,很多源站直接挂了同一张证书; 静态资源路径、跳转 Location 是否暴露内部域名或真实 IP。 证书也建议查一下: echo | openssl s_client -connect 1.2.3.4:443 -servername example.com 2>/dev/null \ | openssl x509 -noout -subject -issuer -dates -ext subjectAltName 2)从日志里反向确认是否有人绕 CDN 打源站 Nginx 访问日志里如果 remote_addr 不是 CDN 节点 IP,却带着正常业务 Host,基本就说明源站已经被扫到或被直连访问了。可以临时统计: awk '$1 !~ /^(CDN_IP段正则)/ {print $1,$7,$11}' /var/log/nginx/access.log | head awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head -30 如果用了 real_ip_header,注意不要被伪造的 X-Forwarded-For 误导。源站只应信任 CDN 出口 IP 段: set_real_ip_from 203.0.113.0/24; set_real_ip_from 198.51.100.0/24; real_ip_header X-Forwarded-For; real_ip_recursive on; 这里的 IP 段要换成你 CDN 厂商官方公布的段,并且定期更新。 3)源站侧直接做访问控制,不要只靠“域名不解析” 最有效的是安全组/防火墙只放行 CDN 回源 IP,其他全部拒绝。Linux 上可以先用 ipset 管理,避免规则太长: ipset create cdn hash:net ipset add cdn 203.0.113.0/24 ipset add cdn 198.51.100.0/24 iptables -I INPUT -p tcp -m multiport --dports 80,443 -m set --match-set cdn src -j ACCEPT iptables -A INPUT -p tcp -m multiport --dports 80,443 -j DROP 如果是云服务器,优先在云安全组做,源站本机防火墙作为第二层。否则攻击流量已经打到主机网卡了。 4)Nginx 层再加一道 Host 校验 即使放行 CDN,也建议默认 server 不返回业务内容,防止通过 IP 或异常 Host 命中站点: server { listen 80 default_server; listen 443 ssl default_server; server_name _; return 444; } server { listen 80; listen 443 ssl; server_name example.com # 正常业务配置 } 如果 CDN 支持自定义回源头,可以在源站校验一个固定 header,降低被人伪造 Host 直连的风险: if ($http_x_origin_token != "your_random_token_here") { return 403; } 这个不能替代 IP 白名单,但作为补充有用,尤其是 CDN IP 段较大、同厂商其他用户也能回源的场景。 5)容易遗漏的暴露点 历史 DNS 记录:旧 A 记录、测试域名、origin.example.com 这类子域; 邮件头、错误堆栈、监控探针返回内容里暴露内网/公网 IP; 对象存储回调、支付回调、Webhook 配置里直接写了源站 IP; 证书透明日志里暴露了源站专用域名; IPv6 忘记接 CDN,只限制了 IPv4。 最后建议做一次外部视角验证:找一台非本机、非 CDN 节点的机器直接访问源站 IP,确认 80/443 无法拿到业务页面;再从 CDN 正常访问,确认回源不受影响。这个闭环比单纯扫 DNS 记录可靠得多。
10月10日10月10日 补一个容易漏的点:源站不只是 80/443,很多“暴露”其实来自旁路端口或历史解析。 我一般会再查这几类: 1)检查源站是否还有管理端口、备用端口开放 比如 8080、8443、8000、9000、9200、5601、3306、6379 这类,CDN 只代理 Web,但源站安全组没收口。 nmap -Pn -sS -sV --top-ports 1000 1.2.3.4 # 针对常见 Web 备用端口 nmap -Pn -sV -p 80,443,8080,8081,8000,8443,8888,9000 1.2.3.4 如果这些端口存在 Web 服务,也要带 Host 头测,不然默认页不一定能看出来: curl -I -H 'Host: example.com' curl -k -I --resolve example.com:8443:1.2.3.4 2)查历史 DNS 和子域名 很多源站不是当前 A 记录暴露,而是以前的解析、测试域名、回源域名没清。 可以重点看这些记录: - 历史 A 记录; - dev、test、staging、origin、admin、api 等子域; - 邮件、监控、对象存储回调里引用的域名; - Git 仓库、前端 JS、配置文件里硬编码的源站地址。 本地可以先简单搜一下前端静态资源: wget -r -l1 -nd -A js -P /tmp/jscheck grep -RniE "([0-9]{1,3}\.){3}[0-9]{1,3}|origin|staging|internal| /tmp/jscheck 3)加固时不要只靠 Nginx 判断 Host Host 校验能挡一部分误访问,但真正有效的还是网络层只允许 CDN 回源 IP。 如果用的是云主机,优先在安全组做白名单;如果只能在机器上做,可以用 ipset 管 CDN IP 段,避免 iptables 规则太长。 示例: ipset create cdn hash:net # 示例,实际替换为 CDN 官方回源 IP 段 ipset add cdn 203.0.113.0/24 ipset add cdn 198.51.100.0/24 iptables -A INPUT -p tcp -m multiport --dports 80,443 -m set --match-set cdn src -j ACCEPT iptables -A INPUT -p tcp -m multiport --dports 80,443 -j DROP 如果是 Nginx,也建议同时加一层 Host 白名单,避免 CDN 被配置错误或被拿来泛域名回源: server { listen 80 default_server; listen 443 ssl default_server; server_name _; return 444; } server { listen 443 ssl; server_name example.com # 正常业务配置 } 4)日志里确认是否还有直连流量 加固前后都建议看一下源站 access log,直接访问源站的请求通常会有几个特征: - remote_addr 不是 CDN 回源 IP; - Host 是 IP、陌生域名或空; - User-Agent 很杂,常见扫描器较多; - 请求路径集中在 /.env、/phpinfo.php、/admin、/wp-login.php 等。 可以临时跑一下: awk '{print $1,$7,$9,$12}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head -50 如果 Nginx 有记录 Host: awk '{print $1,$2,$7,$9}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head -100 5)注意 CDN 回源 IP 变化 不少故障都是白名单写死后,CDN 厂商扩容或区域切换导致回源失败。最好把 CDN 官方 IP 段做成定时更新,并在更新后 reload 防火墙规则。至少要有监控,发现 5xx 或回源失败能及时告警。 最后一个经验:如果业务允许,源站证书可以不要直接使用公网业务证书,改成 CDN 到源站专用证书,或者启用 CDN 的 Authenticated Origin Pull / 回源 mTLS。这样即使源站 IP 被扫到,也不能简单通过 HTTPS Host 直连拿到完整业务响应。
创建帐户或登录后发表意见