发布于3小时前3小时 背景网站接入 CDN 或反向代理后,常见误区是把“域名已走 CDN”当成“源站已经隐藏”。实际授权测试中,经常能看到三类问题:源站公网端口仍对任意地址开放、Nginx 无条件信任客户端提交的 X-Forwarded-For、源站与 CDN 使用相同证书或默认站点,导致资产被关联和绕过。本文以自有域名 example.com、源站 203.0.113.10 为示例,重点讨论服务器边界、加速链路、日志取证与修复。示例地址均为文档保留地址,验证时应替换为明确授权的资产。问题定位先画清实际请求链路:客户端 -> CDN/WAF -> 源站 Nginx -> 应用服务 | +-> access.log / 应用审计日志排查时应分别回答以下问题:源站的 80、443 端口是否只允许 CDN 回源地址访问?知道源站 IP 后,能否通过 Host 或 SNI 直接访问业务?Nginx 是否只信任 CDN 节点写入的真实客户端 IP 请求头?应用读取的是经过校验的 remote_addr,还是任意客户端可伪造的请求头?日志能否同时保留代理地址、原始连接地址和规范化后的客户端地址?一、验证源站是否可以被直接访问在授权测试机上使用 curl 的 resolve 参数,把业务域名临时解析到源站。该方式同时保留正确的 Host 与 TLS SNI,比直接访问 IP 更接近真实绕过路径。curl -vk --resolve example.com:443:203.0.113.10 \ curl -sS --resolve example.com:443:203.0.113.10 \ -D - -o /dev/null CDN 访问和直连源站时的状态码、Server、缓存头、页面长度及证书信息:curl -sS -D cdn.headers -o cdn.body curl -sS --resolve example.com:443:203.0.113.10 \ -D origin.headers -o origin.body sha256sum cdn.body origin.body diff -u cdn.headers origin.headers如果直连源站返回完整业务页面,说明攻击者可能绕过 CDN 的限速、Bot 管理和部分 WAF 规则。即使返回 403,也要检查是否存在其他虚拟主机、备用端口或默认站点泄露。验证应控制请求频率,不进行目录爆破、压力测试或未授权扫描。目标是证明边界缺失,而不是扩大影响。二、检查真实 IP 信任边界错误配置通常表现为应用直接信任 X-Forwarded-For,或 Nginx 将 set_real_ip_from 配置为 0.0.0.0/0。此时外部客户端可能伪造来源地址,影响访问控制、限速、审计和后台白名单。可以先发送一个明显的测试地址,再检查响应、应用日志与 Nginx 日志是否把它误认为真实客户端:curl -sk \ -H 'X-Forwarded-For: 198.51.100.77' \ -H 'X-Real-IP: 198.51.100.77'仅凭响应无法判断是否存在问题,必须结合服务端日志确认。建议临时增加诊断日志格式,同时记录连接源、规范化地址和转发链:log_format edge_audit '$time_iso8601 ' 'remote_addr=$remote_addr ' 'realip_remote_addr=$realip_remote_addr ' 'xff="$http_x_forwarded_for" ' 'host="$host" request="$request" ' 'status=$status bytes=$body_bytes_sent ' 'request_id=$request_id'; access_log /var/log/nginx/edge_audit.log edge_audit;其中 remote_addr 是 realip 模块处理后的地址,realip_remote_addr 保留原始 TCP 对端。若外部测试请求能够让 remote_addr 变成 198.51.100.77,而原始对端并非可信 CDN 节点,则信任边界配置错误。三、Nginx 安全配置思路不要信任全部网络,也不要凭经验填写长期不维护的地址段。应从 CDN 厂商官方接口或文档同步回源网段,并建立变更流程。以下仅为结构示例:# 只能填写 CDN 官方公布并定期同步的地址段 set_real_ip_from 192.0.2.0/24; set_real_ip_from 2001:db8:100::/48; # 请求头名称必须与所用 CDN 的官方约定一致 real_ip_header X-Forwarded-For; real_ip_recursive on; server { listen 443 ssl; server_name example.com; location / { proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Request-ID $request_id; proxy_pass } }如果 CDN 提供专用真实 IP 请求头,优先使用该头,并确保源站只能由 CDN 节点访问。否则,攻击者直连源站时仍可能自行构造同名请求头。四、从网络层限制回源最稳妥的做法是在云安全组、防火墙或上游 ACL 中只允许 CDN 回源网段访问 80、443,运维入口单独走 VPN、堡垒机或管理网。不要只依赖 Nginx 的 allow 和 deny,因为连接仍然到达主机,且配置遗漏时容易失效。使用 nftables 时可以通过集合维护 CDN 地址。执行前应保留现有 SSH 会话,并先确认管理链路,避免把自己锁在服务器外:nft add table inet edge nft 'add set inet edge cdn4 { type ipv4_addr; flags interval; }' nft 'add element inet edge cdn4 { 192.0.2.0/24 }' nft 'add chain inet edge input { type filter hook input priority 0; policy accept; }' nft add rule inet edge input tcp dport { 80, 443 } ip saddr @cdn4 accept nft add rule inet edge input tcp dport { 80, 443 } counter drop nft list table inet edge生产环境应将规则写入配置管理系统,并采用“下载官方网段、校验格式、生成候选配置、语法检查、原子替换、失败回滚”的更新流程。不要直接把远程接口返回内容拼接进防火墙命令。五、限制 Host 与默认虚拟主机源站经常因为默认 server 返回业务内容而暴露。可以设置拒绝型默认虚拟主机,只为合法域名提供服务:server { listen 80 default_server; listen 443 ssl default_server; server_name _; ssl_certificate /etc/nginx/tls/default.crt; ssl_certificate_key /etc/nginx/tls/default.key; return 444; } server { listen 443 ssl; server_name example.com; # 正常业务配置 }该措施不能替代网络层 ACL。因为 curl --resolve 仍能提供正确的 SNI 和 Host,所以只校验域名无法阻止已知源站 IP 的访问。六、日志排查与留痕发生疑似绕过时,可从源站日志中筛选“连接源不属于 CDN 网段”的请求。简单排查可先观察原始连接地址:grep 'realip_remote_addr=' /var/log/nginx/edge_audit.log | tail -n 100 awk '{print $3}' /var/log/nginx/edge_audit.log \ | sort | uniq -c | sort -nr | head实际日志字段可能包含引号和空格,长期分析建议输出 JSON 日志并交给日志平台解析,不要依赖固定列号。至少保留以下字段:字段用途realip_remote_addr确认实际连接到源站的代理或客户端remote_addr记录经过可信代理链规范化后的客户端地址X-Forwarded-For 原值分析伪造、重复追加和代理链异常Host、SNI、请求路径判断虚拟主机绕过与资产探测request_id关联 CDN、Nginx 与应用日志状态码与响应字节数识别直连成功及异常响应差异日志应设置访问权限、轮转和集中留存。安全事件期间避免立即覆盖原始日志;导出后计算哈希,并记录时间范围、时区与采集命令,便于后续复盘。风险判定高风险:源站可直接访问,且 CDN/WAF 承担认证、限速或漏洞拦截职责。高风险:任意客户端可以伪造真实 IP,并影响后台白名单、支付风控或管理接口访问控制。中风险:源站只泄露默认页面、证书或版本信息,但尚未证明业务绕过。低风险:源站网络层已限制为 CDN 网段,真实 IP 头仅在可信连接上解析,日志链路完整。修复与复测清单在云安全组或上游防火墙限制 80、443 的回源来源。仅信任 CDN 官方网段,禁止 set_real_ip_from 0.0.0.0/0。为 CDN 网段更新设置自动化校验、审批和回滚。默认虚拟主机拒绝未知 Host,但不把它当作唯一防线。应用只使用反向代理规范化后的客户端地址,不直接读取任意转发头做鉴权。同时记录原始 TCP 对端和规范化客户端 IP,使用 request_id 串联多层日志。修复后分别从公网测试机、CDN 节点链路和运维网络复测,确认直连被拒绝且正常业务未受影响。总结CDN 解决的是加速与边缘防护问题,不会自动完成源站隔离。有效的防护需要三层同时成立:网络层只接受合法回源、代理层严格定义真实 IP 信任边界、日志层保留足够的原始证据。授权验证时,用少量可复现请求证明问题,再结合配置和日志给出修复闭环,通常比大范围扫描更有价值。 网络请求、日志与边界流量分析示意 服务器、云资产与权限边界梳理示意
2小时前2小时 这类排查建议拆成三条证据链:源站是否可直连、应用拿到的客户端 IP 是否可信、日志能否把 CDN 与源站请求串起来。三者不要混在一起判断。 1. 先确认源站暴露面 除当前 DNS 外,还要检查历史解析、旧子域、邮件记录、证书透明度记录,以及源站是否对任意 Host 返回业务内容。拿到候选 IP 后,在授权范围内绕过 DNS 验证: curl -skv --resolve \ -o /dev/null openssl s_client -connect 203.0.113.10:443 \ -servername </dev/null 如果直连候选 IP 仍能得到正常证书和业务页面,说明 CDN 只是代理层,并没有形成源站访问边界。修复重点不是隐藏 IP,而是源站防火墙仅允许 CDN 回源网段,并将运维入口放到 VPN、堡垒机或独立管理网络。 # 示例:先在维护窗口验证,避免遗漏 CDN 网段导致断站 iptables -A INPUT -p tcp --dport 443 -s 192.0.2.0/24 -j ACCEPT iptables -A INPUT -p tcp --dport 443 -s 198.51.100.0/24 -j ACCEPT iptables -A INPUT -p tcp --dport 443 -j DROP CDN 网段应从厂商接口或官方清单自动同步,不建议长期手工维护。源站默认虚拟主机也应直接拒绝请求,避免扫描 IP 时泄露站点: server { listen 443 ssl default_server; server_name _; return 444; } 2. “真实 IP”只能在可信代理边界内解析 不能直接把 X-Forwarded-For 第一项当客户端地址,因为直连源站的请求可以自行伪造该头。Nginx 应只信任 CDN 出口网段,并使用厂商明确约定的请求头: set_real_ip_from 192.0.2.0/24; set_real_ip_from 198.51.100.0/24; real_ip_header X-Forwarded-For; real_ip_recursive on; 若厂商提供专用头,例如 CF-Connecting-IP 或类似字段,优先按其文档配置,但前提仍是 TCP 对端地址属于 CDN 网段。应用侧建议同时保留: remote_addr:解析后的客户端 IP; realip_remote_addr:实际连接源站的代理 IP; 原始转发头:用于审计,不直接作为鉴权依据。 限流、封禁、审计和登录风控都应使用经过可信代理链计算后的地址。若架构中还有 SLB、Ingress 或 Service Mesh,需要按从源站向外的顺序逐跳列出可信代理,不能简单信任全部内网段。 3. 日志要保留“连接来源、解析结果、链路标识” log_format trace '$time_iso8601 ' 'client=$remote_addr ' 'peer=$realip_remote_addr ' 'xff="$http_x_forwarded_for" ' 'host="$host" request="$request" status=$status ' 'reqid="$request_id" cdnid="$http_cf_ray" ' 'rt=$request_time urt=$upstream_response_time'; access_log /var/log/nginx/access_trace.log trace; 排查时重点找三类异常: # 实际连接源站的地址不在 CDN 网段 awk '{print $3}' /var/log/nginx/access_trace.log | sort | uniq -c | sort -nr # 同一客户端字段对应大量不同 peer,检查代理链或头部伪造 grep 'client=' /var/log/nginx/access_trace.log | tail -n 200 # 查看是否存在直接按 IP、异常 Host 访问 grep -E 'host="([0-9]{1,3}\.){3}[0-9]{1,3}"|host="_"' \ /var/log/nginx/access_trace.log 更稳妥的验证方法是在源站防火墙日志、Nginx 日志和 CDN 边缘日志中用时间窗口、请求 ID、URI、状态码进行交叉比对。仅凭 X-Forwarded-For 无法证明请求经过 CDN;TCP 对端、CDN 请求 ID 和边缘日志三者能对应上,证据才比较完整。 最后还要检查是否存在绕过入口,例如旧的 A 记录、IPv6 AAAA、未纳入 CDN 的 API/静态资源子域、对象存储源地址和源站健康检查端口。实际案例里,遗漏 AAAA 或旧测试子域比主域 DNS 暴露更常见。
创建帐户或登录后发表意见