发布于10月9日10月9日 在网站建设和服务器运维中,CDN、WAF、反向代理与源站通常会形成多层访问链路。实际授权测试里,比较常见的问题不是某个单点漏洞,而是加速层、源站访问控制和应用权限判断之间没有对齐:公网域名经过 CDN 后访问正常,但源站 IP 仍然可以直接访问;应用又错误信任客户端提交的 X-Forwarded-For,导致后台限制、审计记录和安全策略出现偏差。下面以“网站使用 CDN 加速,源站为 Nginx + 应用服务,后台仅允许办公网访问”的场景为例,说明如何在授权范围内定位问题、验证影响并修复。测试目标是确认访问边界和日志完整性,不涉及数据导出、持久化或破坏性操作。一、场景与测试边界业务域名: CDN 或云 WAF 对外提供服务。源站:公网 IP 为 203.0.113.20,Nginx 监听 80/443,后端应用监听本机 127.0.0.1:8080。管理入口:/admin/,设计要求仅办公网 10.20.0.0/16 可访问。授权范围:指定域名、指定源站 IP 和测试时间窗口;只验证 HTTP 状态、响应头、固定的健康检查接口,不读取业务数据。验证前应记录授权域名、源站地址、测试账号、允许的请求速率和禁止操作。尤其不要把“发现源站 IP”直接等同于“可以继续访问任意内部服务”,后续每一步都应重新确认范围。二、问题定位:先确认请求到底经过了哪一层第一步是比较正常域名访问和直接访问源站时的响应差异。建议使用固定的公开页面或健康检查接口,并保留响应头、状态码和时间,不要批量抓取站点内容。#!/usr/bin/env bash set -u DOMAIN=' ORIGIN='203.0.113.20' PATH='/healthz' printf '%s\n' '--- access through CDN/WAF ---' curl -skS --max-time 8 -D - " -o /dev/null printf '%s\n' '--- direct access to origin with Host header ---' curl -skS --max-time 8 --resolve "${DOMAIN}:443:${ORIGIN}" \ -D - " -o /dev/null printf '%s\n' '--- direct IP without expected Host ---' curl -skS --max-time 8 -D - " -o /dev/null重点观察以下结果:源站直连是否返回与正常域名相同的业务内容,而不是默认站点或拒绝页面。响应头中是否泄露源站标识、内部版本、上游名称或调试信息。直接访问源站时,TLS 证书、虚拟主机和 SNI 是否配置正确。CDN 回源请求是否使用固定的回源标识,源站是否只允许 CDN 出口网段访问。--resolve 会让 curl 将域名解析到指定地址,同时保留原始 Host 和 TLS SNI,比直接请求 IP 更接近真实的源站访问场景。测试时不要依赖修改本机 hosts 后进行大量请求,避免误伤其他环境。三、常见根因一:源站把任意来源都当成可信代理很多应用会根据 X-Forwarded-For 判断客户端 IP,Nginx 也可能通过 real_ip_header 改写日志中的地址。如果没有限制“哪些上游代理可以提交该请求头”,攻击者可以自行构造一个看似来自办公网的地址,造成两类问题:应用层 IP 白名单被绕过。访问日志记录的客户端地址不可信,事件调查无法还原真实来源。在 Nginx 中,set_real_ip_from 必须只填写实际 CDN/WAF 的出口网段,不能配置为全网,也不能仅因为某个请求头名称看起来可信就直接使用。# /etc/nginx/conf.d/realip.conf # 示例网段,实际值必须以厂商公布并经过变更审批的回源网段为准 set_real_ip_from 198.51.100.0/24; set_real_ip_from 2001:db8:100::/48; real_ip_header X-Forwarded-For; real_ip_recursive on; log_format main_secure '$remote_addr original=$realip_remote_addr ' 'xff="$http_x_forwarded_for" ' 'request_id=$request_id ' 'host=$host request="$request" ' 'status=$status bytes=$body_bytes_sent ' 'upstream_status=$upstream_status ' 'request_time=$request_time';这里保留了 $realip_remote_addr 和原始 X-Forwarded-For,便于发生争议时区分“经过可信代理还原的地址”和“客户端提交的请求头”。配置修改后先执行语法检查,再平滑加载:sudo nginx -t sudo systemctl reload nginx sudo nginx -T | sed -n '/realip.conf/,+20p'不同 CDN 对真实客户端地址的请求头名称可能不同,例如 True-Client-IP 或厂商自定义字段。不能直接照抄配置,应以链路抓包、厂商文档和实际回源请求为准,并确保该请求头在源站防火墙层面只接受来自可信回源地址的流量。四、常见根因二:后台只做了应用层限制,没有做服务器层限制后台入口如果只在 Java、PHP 或 Node.js 代码里判断客户端地址,通常还会受到代理头解析、IPv4/IPv6 表示方式和多级代理链的影响。更稳妥的做法是将限制下沉到源站反向代理,再由应用做第二层校验。server { listen 443 ssl http2; server_name # 默认拒绝后台入口 location ^~ /admin/ { allow 10.20.0.0/16; allow 192.0.2.10; deny all; proxy_set_header Host $host; proxy_set_header X-Request-ID $request_id; proxy_pass } location = /healthz { access_log off; proxy_pass } location / { proxy_set_header Host $host; proxy_set_header X-Request-ID $request_id; proxy_pass } }在授权验证中,可以只访问 /admin/ 首页,观察允许网段和非允许网段的状态码是否符合预期。不要尝试登录、修改配置或提交表单。若测试环境必须验证应用层权限,应使用专门的低权限测试账号,并提前准备可回滚的测试数据。需要特别检查 IPv6。只配置 IPv4 白名单而未配置 IPv6 监听或防火墙规则,可能导致相同域名解析到 IPv6 时出现一条未受保护的访问路径。可用以下命令核对:dig +short A dig +short AAAA ss -lntp | grep -E ':(80|443|8080)\b' sudo nft list ruleset五、验证“源站暴露”是否已经形成实际风险源站 IP 可访问本身不一定是高危问题,关键要看是否绕过了原有的安全控制。建议按以下顺序进行低影响验证:访问固定健康检查接口,确认源站是否响应。访问公开页面,比较 CDN 与源站的缓存、认证和安全响应头。仅对后台目录发送一次 GET,比较办公网和非办公网的状态码。使用测试请求头验证应用是否错误信任客户端提供的代理信息,但不进入后台、不提交数据。在 Nginx、WAF、应用和 CDN 日志中按请求 ID 对齐记录,确认请求来源是否可追溯。可以将结果整理成“路径—入口—认证—网络限制—日志字段”的矩阵。例如:路径CDN 访问源站直连非办公网访问日志可追溯性/healthz200应拒绝或仅允许回源网段不适用应有 request_id/admin/应由源站 ACL 控制应拒绝403记录真实连接地址与代理链/200不应绕过 WAF 策略按业务规则记录 Host、状态码、耗时六、日志排查:判断是否存在绕过和误报发现问题后,不能只看应用日志。应同时核对 CDN、源站访问日志、WAF 告警、系统防火墙和应用认证日志。重点关注同一时间段内的以下模式:同一用户会话在 CDN 日志中来自公网地址,但应用日志中显示为办公网地址。源站 IP 收到大量带正确 Host 的请求,而 CDN 日志中没有对应记录。请求头中出现多个逗号分隔的代理地址,应用却只记录了最左侧或最右侧地址。后台返回 200,但 WAF 或源站 ACL 没有对应的放行记录。# 按时间、状态码和请求路径初步筛选 Nginx 日志 sudo awk '$0 ~ /request="GET \/admin\// && $0 ~ /status=200/' \ /var/log/nginx/access.log | tail -n 100 # 检查是否有源站 IP 直连但没有对应 CDN 标识的请求 sudo grep -E 'host=www\.example\.test' /var/log/nginx/access.log \ | grep -v 'cdn-request-id' | tail -n 100 # systemd 环境下查看最近的拒绝记录 sudo journalctl -u nginx --since '2 hours ago' \ | grep -Ei 'denied|forbidden|access forbidden'生产环境不建议只依赖文本 grep。更可靠的做法是将 CDN、WAF、Nginx 和应用日志统一写入日志平台,使用 request_id 或 W3C Trace Context 进行关联,并为源站直连、代理头异常、后台拒绝和证书不匹配建立告警。七、修复建议与复测标准网络层:在安全组、防火墙或负载均衡层限制源站入站来源,仅允许 CDN/WAF 回源网段和必要的运维出口;关闭不必要的公网端口。虚拟主机层:为未知 Host 配置默认拒绝站点,避免通过 IP 或任意 Host 命中业务虚拟主机。代理头层:只信任来自明确代理网段的真实客户端地址,并保留原始连接地址用于审计。应用层:后台同时使用身份认证、角色授权和网络边界控制,不能把 IP 白名单当成唯一认证因素。日志层:统一记录 request ID、连接地址、还原后的客户端地址、原始代理头、Host、状态码和上游状态,避免记录密码、令牌等敏感内容。配置变更:执行 nginx -t 后再 reload,并在变更后从 CDN、办公网、普通公网和 IPv6 四个视角复测。复测至少应满足:源站直连不再绕过 CDN/WAF 策略;非办公网访问后台得到明确拒绝;伪造代理头不会改变源站 ACL 和应用权限判断;CDN、源站与应用日志能够通过同一请求 ID 对齐;健康检查、缓存和证书配置不因修复而中断。总结网站加速链路中的安全问题,通常不是“有没有 CDN”这么简单,而是要看 CDN、源站、应用和日志对客户端身份的定义是否一致。授权测试时应从固定接口和低影响请求开始,先确认入口关系,再验证权限边界,最后用日志还原完整链路。对于源站暴露、代理头信任和后台访问控制这类问题,网络层拒绝、Nginx 访问控制、应用授权和审计关联需要同时落地,单独修补其中一层往往仍会留下绕过路径。 网络请求、日志与边界流量分析示意
10月9日10月9日 这类场景我一般会把排查拆成两条线:一条确认“源站是否能被绕过加速链路访问”,另一条确认“源站信任边界有没有放错位置”。不要只看域名解析当前是不是 CNAME 到加速厂商,很多暴露点在历史记录、证书、备用域名和回源配置里。 1)先确认有没有直接命中源站的路径 # 当前解析链路 dig +short example.com dig +short dig +trace # 常见备用域名 / 回源域名 for d in origin old api admin test dev static img; do dig +short $d.example.com done # 如果怀疑某个 IP 是源站,用 Host 头直接打 curl -k -I --resolve curl -k -I -H 'Host: 判断是不是源站不要只看状态码,可以对比这些特征: Server、X-Powered-By、自定义 header 是否和加速链路不同; 错误页样式是否暴露 Nginx/Apache/Tomcat 默认页; 同一路径下响应时间、缓存头、TLS 证书 SAN 是否异常; 访问管理后台、上传目录、健康检查接口是否只有源站能看到。 2)源站入口应只允许加速厂商回源 IP 如果源站公网 IP 必须保留,至少在安全组 / 防火墙 / Nginx 三层做限制。Nginx 可以这样做兜底: server { listen 443 ssl; server_name # 只允许 CDN 回源 IP 段,示例需替换成真实厂商网段 allow 203.0.113.0/24; allow 198.51.100.0/24; deny all; location / { proxy_pass } } 更推荐在云安全组层面限制,因为 Nginx 规则改错时仍可能漏;源站如果还有 80、8080、8443、管理端口,也要一起收口,不要只关 443。 3)重点看真实 IP 透传的信任边界 很多权限边界问题不是源站暴露本身,而是应用错误信任了 X-Forwarded-For。如果后端用这个头判断内网、白名单、管理员 IP,就很容易被伪造。 Nginx 正确做法是只信任加速节点传来的真实 IP: # 仅信任 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; 同时应用层不要直接取用户可控 header: # 不建议 client_ip = request.headers.get("X-Forwarded-For") # 更稳妥:由反向代理清洗后传入,应用只取可信字段 client_ip = request.headers.get("X-Real-IP") or request.remote_addr 可以用下面方式验证是否存在伪造风险: curl -I \ -H 'X-Forwarded-For: 127.0.0.1' \ -H 'X-Real-IP: 127.0.0.1' 如果加这些 header 后权限、限流、后台访问结果发生变化,基本就是边界判断放错了。 4)补充几个容易漏的暴露面 证书透明日志里出现过 origin.example.com、admin.example.com 这类域名; 邮件、对象存储、监控回调里泄露过源站 IP; 历史 DNS 记录仍能查到旧 A 记录; IPv6 没接 CDN,但 AAAA 记录直连源站; 加速只覆盖主站,API、后台、静态资源域名没覆盖。 最后建议落地成一个最小闭环:源站安全组只放行 CDN 回源 IP,应用不要信任客户端传入的 IP 头,后台接口额外加鉴权或 VPN/零信任入口,不要把“经过加速链路”当成权限边界。这样即使源站 IP 被枚举出来,也很难形成实际绕过。
10月10日10月10日 补一条我觉得很容易漏的:源站暴露本身是一回事,真正危险的是应用把“来自加速链路的标识”当成了权限边界。 可以重点测这几类头是否被源站直接信任: X-Forwarded-For X-Real-IP X-Forwarded-Proto Forwarded CF-Connecting-IP True-Client-IP X-Client-IP 如果后台、管理接口、限流、风控是按这些头判断客户端 IP,一旦源站可直连,就可能被伪造。 可以这样对比测试: # 走正常加速链路 curl -i # 如果已确认某个 IP 疑似源站,直连源站并伪造来源 IP curl -k -i --resolve example.com:443:1.2.3.4 \ -H 'X-Forwarded-For: 127.0.0.1' \ -H 'X-Real-IP: 127.0.0.1' \ # 测试内网白名单绕过 curl -k -i --resolve example.com:443:1.2.3.4 \ -H 'X-Forwarded-For: 10.0.0.1' \ 如果响应从 403 变成 200,或者返回内容差异明显,就说明权限判断放错层了。 源站侧建议不要无条件信任这些头,只信任来自 CDN / 反代节点的转发头。Nginx 可以这样收口: # 只信任 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; # 非 CDN 来源直接拒绝 geo $from_cdn { default 0; 203.0.113.0/24 1; 198.51.100.0/24 1; } server { listen 443 ssl; server_name example.com; if ($from_cdn = 0) { return 403; } location / { proxy_pass } } 更稳一点的做法是源站再加一层“回源密钥头”,CDN 回源时加固定 header,源站校验: # 源站 Nginx map $http_x_origin_token $valid_origin { default 0; "your-random-token" 1; } server { listen 443 ssl; if ($valid_origin = 0) { return 403; } location / { proxy_pass } } 注意这个只能作为补充,不能替代源站防火墙放行 CDN IP。因为密钥一旦在日志、错误页、调试接口里泄露,就失效了。 排查时我一般还会看这几个点: 安全组 / 防火墙是否仍然对公网开放 80、443、8080、8443、9000 等端口; 后台路径是否只在 CDN WAF 上拦截,源站本身没有鉴权; 限流是否只在 CDN 层做,直连源站是否不受限制; 应用日志里真实客户端 IP 是否异常,比如大量请求都变成 CDN 节点 IP,说明 real_ip 没配好; 健康检查、灰度域名、老负载均衡地址是否绕过了主链路策略。 源站最终目标不是“藏起来就行”,而是即使 IP 被知道,也只能被 CDN / 反代按预期访问,权限判断也不依赖客户端可伪造的 header。
创建帐户或登录后发表意见