发布于21小时前21小时 背景网站接入 CDN、反向代理或云加速服务后,访问链路通常变为“客户端 → CDN/WAF → Nginx → 应用”。这类架构能降低源站压力,但也容易引入三类边界问题:源站仍可被公网直接访问、动态响应被错误缓存、应用无条件信任客户端提交的代理请求头。下面以一次已获授权的网站安全检查为例,说明如何验证这些问题,并给出 Nginx 层面的修复方法。测试前应明确授权域名、源站 IP、允许的请求频率和时间窗口,不对第三方 CDN 节点做超出范围的扫描。问题定位目标站点通过 CDN 对外提供服务,但运维反馈应用日志中出现了无法对应 CDN 节点的访问 IP,同时部分登录用户偶尔看到其他账号的页面片段。初步判断需要分别检查源站访问控制、缓存键设计以及真实客户端 IP 的解析方式。源站是否只允许 CDN 回源地址访问。登录态、用户中心、订单接口是否进入了共享缓存。Nginx 是否直接信任 X-Forwarded-For 或 X-Real-IP。CDN 日志、Nginx access log 与应用审计日志能否通过请求标识关联。一、确认请求实际经过 CDN先对授权域名执行低频请求,记录解析结果、TLS 证书和响应头。Server、Via、Age、X-Cache 等字段只能作为线索,不能单独作为结论,因为这些字段可能被隐藏或伪造。dig +short A curl -skI curl -skI --http2 openssl s_client -connect -servername </dev/null 2>/dev/null \ | openssl x509 -noout -subject -issuer -ext subjectAltName如果已由资产所有者提供源站 IP,可使用 Host 头和 SNI 做定向验证,不需要通过扫描或第三方历史数据库猜测源站。下面的命令只适用于明确授权的源站地址:ORIGIN_IP=192.0.2.10 curl -skI --resolve \ curl -sk --resolve \ -o /dev/null \ -w 'status=%{http_code} remote=%{remote_ip}\n'如果通过源站 IP 加 Host/SNI 后能够获得与 CDN 域名一致的业务页面,说明源站存在绕过 CDN 直接访问的可能。此时还应比较安全头、限速策略和错误页,确认直接访问是否同时绕过了 WAF 或鉴权前置层。二、检查动态页面的缓存边界缓存越权常见于缓存键未包含必要的身份维度,或者 Nginx/CDN 忽略了响应中的 Set-Cookie、Cache-Control。验证时应使用专用测试账号和无敏感数据的测试记录,禁止读取真实用户内容。使用两个已授权测试账号分别请求同一路径,重点观察 Age、X-Cache、ETag、Vary 和 Cache-Control。响应正文不要直接 diff 全量敏感内容,可以仅计算哈希并检查测试账号标记。curl -skD a.headers \ -H 'Cookie: session=TEST_ACCOUNT_A' \ ' \ -o a.body curl -skD b.headers \ -H 'Cookie: session=TEST_ACCOUNT_B' \ ' \ -o b.body sha256sum a.body b.body grep -iE '^(age|x-cache|cache-control|vary|set-cookie|etag):' a.headers b.headers grep -Eo 'TEST_ACCOUNT_[AB]|marker=[AB]' a.body b.body若账号 B 的响应命中了账号 A 生成的缓存,或携带 Cookie 的请求仍出现共享缓存命中,应立即停止扩大验证范围,保存请求时间、请求标识和最小化证据。不要通过枚举账号、订单号等方式证明影响。Nginx 使用 FastCGI 缓存时,可以显式跳过登录态和敏感路径:map $http_cookie $has_session { default 0; ~*"(session|auth_token|jwt)=" 1; } map $request_uri $sensitive_path { default 0; ~*^/(login|logout|account|admin|api/private)(/|$) 1; } server { location / { set $skip_cache 0; if ($has_session) { set $skip_cache 1; } if ($sensitive_path) { set $skip_cache 1; } fastcgi_cache SITE_CACHE; fastcgi_cache_bypass $skip_cache; fastcgi_no_cache $skip_cache; fastcgi_cache_valid 200 5m; add_header X-Cache-Status $upstream_cache_status always; } }Nginx 的 if 使用场景需要谨慎。生产环境也可以通过 map 组合出单一变量,或按敏感路径拆分 location。关键不是照搬配置,而是确保任何带身份状态的响应都不进入共享缓存。应用层还应对用户数据响应返回明确策略:Cache-Control: private, no-store Pragma: no-cache Vary: Cookie, AuthorizationVary 不能替代 no-store。对于账号、订单、后台和私有 API,优先选择不缓存,而不是把完整 Cookie 或 Authorization 放入缓存键,这会增加缓存碎片和令牌泄露风险。三、排查真实客户端 IP 信任链很多应用直接读取 X-Forwarded-For 的第一个值。如果源站可直连,攻击者可以自行构造该请求头,造成审计记录伪造、IP 白名单绕过或限速失效。先查看 Nginx 的实际生效配置,而不是只检查某一个配置文件:sudo nginx -T 2>&1 | grep -nE 'set_real_ip_from|real_ip_header|real_ip_recursive|log_format|allow|deny' curl -skI \ -H 'X-Forwarded-For: 127.0.0.1, 198.51.100.20' \ -H 'X-Real-IP: 127.0.0.1'测试请求本身不能证明存在绕过,还需由运维在日志中确认 remote_addr、http_x_forwarded_for 和最终解析后的 realip_remote_addr。安全配置应只信任 CDN 官方公布并定期同步的回源网段:# 示例地址仅用于文档,生产环境替换为 CDN 官方回源网段 set_real_ip_from 203.0.113.0/24; set_real_ip_from 2001:db8:1234::/48; real_ip_header X-Forwarded-For; real_ip_recursive on; # 不要使用以下配置:它会信任任意来源 # set_real_ip_from 0.0.0.0/0;如果 CDN 使用自定义真实 IP 头,应以供应商文档为准,并在源站网络层限制只有 CDN 回源地址可以连接。仅配置 real_ip 模块不能阻止源站被直连。四、建立可关联的日志留痕排障时最有价值的是一条请求在 CDN、Nginx 和应用之间具有同一个请求标识。客户端提交的 Request-ID 不应直接作为可信审计字段,建议由边缘层生成,源站校验格式后继续传递。log_format security_json escape=json '{"time":"$time_iso8601",' '"request_id":"$request_id",' '"remote_addr":"$remote_addr",' '"realip_peer":"$realip_remote_addr",' '"xff":"$http_x_forwarded_for",' '"host":"$host","method":"$request_method",' '"uri":"$uri","args":"$args",' '"status":$status,"bytes":$body_bytes_sent,' '"request_time":$request_time,' '"upstream_time":"$upstream_response_time",' '"cache":"$upstream_cache_status",' '"ua":"$http_user_agent"}'; access_log /var/log/nginx/security_access.json security_json; add_header X-Request-ID $request_id always; proxy_set_header X-Request-ID $request_id;日志中不应记录 Cookie、Authorization、完整 POST 正文和密码重置令牌。查询参数可能包含令牌或个人信息,条件允许时应在应用层记录规范化路由而不是原始参数。可以按授权测试时间窗口快速筛选源站直连、伪造代理头和异常缓存命中:# 检查近期是否存在带 XFF 但连接来源不属于预期代理的记录 jq -r 'select(.xff != "" and .realip_peer != "") | [.time,.request_id,.remote_addr,.realip_peer,.xff,.host,.uri,.status,.cache] | @tsv' \ /var/log/nginx/security_access.json | tail -n 100 # 统计敏感路径上的缓存状态 jq -r 'select(.uri | test("^/(account|admin|api/private)")) | .cache' \ /var/log/nginx/security_access.json | sort | uniq -c # 配置检查和安全重载 sudo nginx -t && sudo systemctl reload nginx五、源站访问控制与权限边界推荐在云安全组或主机防火墙上限制 80、443 端口的来源,仅允许 CDN 回源网段和独立管理出口。Nginx 的 allow/deny 可以作为第二层控制,但不应代替网络层策略。server { listen 443 ssl; server_name # 示例网段,实际配置必须使用 CDN 官方地址清单 allow 203.0.113.0/24; allow 2001:db8:1234::/48; deny all; location = /health { access_log off; return 200 'ok\n'; } }还应单独检查默认虚拟主机。若未知 Host 仍被路由到业务站点,直接访问源站 IP 时可能泄露页面或证书信息:server { listen 80 default_server; listen 443 ssl default_server; server_name _; return 444; }上线前应确认健康检查来源,否则收紧安全组或 deny 规则后可能导致 CDN 将源站判定为不可用。建议先在日志中观察至少一个完整业务周期,再灰度启用阻断。风险判断问题直接影响优先级源站公网直连绕过 CDN、WAF、限速和统一证书策略高私有响应进入共享缓存跨用户数据泄露或页面内容串号严重无条件信任代理头日志伪造、IP 白名单或限速边界失效高缺少统一请求标识事件无法跨层关联,增加取证成本中日志记录令牌和 Cookie日志系统成为二次泄露源高修复与复测建议在安全组和防火墙层限制源站端口,只允许 CDN 回源网段;建立地址清单自动更新和变更审计。对登录、后台、订单、用户中心和私有 API 设置 private, no-store,并在 CDN 与 Nginx 两层同时验证。只信任明确的代理网段;保留连接对端地址、代理头原值和解析后地址,避免覆盖取证字段。默认虚拟主机拒绝未知 Host,业务虚拟主机启用明确的 server_name。使用两个隔离测试账号复测缓存,不接触真实用户数据;复测后清理测试缓存与测试会话。修复前后分别保存 nginx -T、响应头、请求标识和最小化日志片段,便于回滚和审计。总结网站加速并不是简单地把域名解析到 CDN。真正需要关注的是回源访问边界、缓存身份边界、代理头信任边界和日志关联能力。排查时应从请求链路逐层验证:先确认入口,再验证源站是否可直连,然后检查动态缓存与真实 IP 解析,最后通过统一请求标识闭合证据链。这样既能控制授权测试影响,也能让修复结果具备可验证性。 网络请求、日志与边界流量分析示意 服务器、云资产与权限边界梳理示意
19小时前19小时 这类排查建议分成三条证据链:源站暴露面、缓存隔离、边缘到源站的日志关联。只看页面内容或响应头相似度,容易把同一套默认站点误判成真实源站。 1. 源站识别要以“自有资产核验”为准 先检查现网 DNS、历史解析、证书签发记录和云资产清单,重点找 CDN 接入前遗留的 A/AAAA 记录、测试域名、邮件头或监控平台中暴露的地址: dig +short example.com A dig +short example.com AAAA dig +short CNAME # 在确认属于自有资产后,核验候选地址是否承载该虚拟主机 curl -kI --resolve \ openssl s_client -connect 203.0.113.10:443 \ -servername </dev/null 2>/dev/null | openssl x509 -noout -subject -issuer -ext subjectAltName 证书、标题和静态文件哈希只能作为线索,最终应回到源站访问日志或负载均衡日志确认。更重要的是验证非 CDN 出口能否直连:正确状态应为超时、拒绝连接或明确的 403,而不是仍能拿到业务页面。 源站侧至少做两层限制:安全组只放行 CDN 官方回源网段;回源请求再使用 CDN 重写的专用请求头或 mTLS。不要只依赖可由客户端伪造的 X-Forwarded-For。 # Nginx 示例:网络层白名单仍应放在安全组/防火墙 allow 192.0.2.0/24; # 替换为 CDN 官方回源网段 allow 2001:db8:100::/48; deny all; # 辅助校验,密钥应由 CDN 在回源阶段覆盖,不能透传客户端同名头 if ($http_x_origin_verify != "随机长值") { return 403; } 2. 缓存越权不要只测“是否命中” 建议准备两个权限不同的测试账号,对同一 URL 交叉请求,并记录 Age、Cache-Status、X-Cache、响应体哈希及账号标识。高风险信号是:带认证信息的首个请求产生缓存,第二个账号或匿名请求随后命中相同对象。 # A 账号先请求 curl -skD a.hdr -o a.body \ -H 'Authorization: Bearer TOKEN_A' \ ' # 匿名及 B 账号复测 curl -skD anon.hdr -o anon.body \ ' curl -skD b.hdr -o b.body \ -H 'Authorization: Bearer TOKEN_B' \ ' sha256sum a.body anon.body b.body grep -Ei '^(age|cache-status|x-cache|set-cookie|cache-control|vary):' \ a.hdr anon.hdr b.hdr 需要同时检查缓存键是否遗漏了主机名、关键查询参数、设备分流头和内容协商头。尤其注意 CDN 是否忽略查询串、是否把扩展名当成静态资源、是否缓存 302,以及源站返回 Set-Cookie 时边缘节点仍强制缓存。 认证接口的原则应是默认不缓存,而不是试图把 Cookie 或 Authorization 全部塞进缓存键: proxy_cache_bypass $http_authorization $cookie_session; proxy_no_cache $http_authorization $cookie_session; # 动态接口由应用返回 Cache-Control: private, no-store Vary: Authorization, Cookie Vary 是否被具体 CDN 纳入缓存键要实际确认,不能只看源站响应头。若业务必须缓存个性化内容,建议只缓存剥离身份信息后的公共片段,并由边缘计算显式构造有限、可审计的缓存键。 3. 日志要能从 CDN 一路关联到源站 边缘节点应生成不可由客户端覆盖的请求 ID,回源时透传;源站同时记录 CDN 请求 ID、实际对端地址、规范化后的客户端地址、缓存状态和上游耗时。Nginx 可以先这样落 JSON 日志: log_format cdn_json escape=json '{"time":"$time_iso8601",' '"request_id":"$request_id",' '"cdn_id":"$http_x_cdn_request_id",' '"peer":"$remote_addr",' '"xff":"$http_x_forwarded_for",' '"host":"$host","method":"$request_method","uri":"$request_uri",' '"status":$status,"bytes":$body_bytes_sent,' '"cache":"$upstream_cache_status",' '"upstream":"$upstream_addr","urt":"$upstream_response_time",' '"ua":"$http_user_agent"}'; access_log /var/log/nginx/cdn_access.json cdn_json; 配置真实 IP 模块时,只信任 CDN 回源网段,否则攻击者直连源站后可伪造客户端地址: set_real_ip_from 192.0.2.0/24; set_real_ip_from 2001:db8:100::/48; real_ip_header X-Forwarded-For; real_ip_recursive on; 排查时按请求 ID关联 CDN 日志和源站日志;如果同一 ID 只存在于源站,通常是绕过 CDN 或头部伪造;如果只存在于 CDN 且显示缓存命中,则源站没有日志属于正常情况。对疑似直连事件,还应查询安全组流日志或防火墙日志,确认对端地址、目标端口和首次出现时间。 整改后的验收标准可以定得简单一些:公网非 CDN 地址无法访问源站;认证响应不会进入共享缓存;任意一次测试请求都能在边缘日志中定位,并在发生回源时关联到唯一的源站记录。
创建帐户或登录后发表意见