YesWeHack 安全研究者 Alex Brumen 披露了一类新的 Web 缓存攻击手法,命名为「缓存键注入」(cache key injection)。与以往针对未纳入缓存键的请求头做文章的缓存投毒不同,它直接攻击缓存键本身:当 Nginx 之类的缓存把多个变量无分隔符拼接成键时,两个完全不同的请求可能产生同一个键,进而绕过访问控制、把 404 缓存成正常页面,甚至投毒出存储型 XSS。
事件概述
问题出在缓存键的构造方式。典型配置会把协议、主机、请求 URI 与部分请求头拼成一个字符串再计算哈希,例如 proxy_cache_key "$scheme$proxy_host$uri$is_args$args$http_accept"。这些片段之间没有任何分隔符,给攻击者在片段边界上做位移留下了空间。Brumen 证明,把 /admin 拆成 /ad 路径加一个 Accept: min*/* 请求头,就能与合法请求生成完全相同的缓存键,从而命中本不该被外部访问的缓存条目。
技术细节
研究者给出了三条利用路径:
- 无需受害者交互的 Web 缓存欺骗:把受限路径拆到 URI 与 Accept 头中,绕过基于 IP 的访问控制列表,直接读到已缓存的管理后台响应。
- 缓存投毒拒绝服务(CPDoS):先请求一个残缺路径,例如 /h 配 Accept: ome*/*,让源站返回 404,而该响应落在与 /home 相同的键上,此后所有正常访客都会拿到错误页,直到缓存过期或被手工清理。
- 协议混淆导致的存储型 XSS:当缓存键以 $scheme$host 开头、且后端会把 Host 回显到 script 标签的 src 时,攻击者可用明文 HTTP 把 https 结尾的字母 s 位移到自造主机名(如 sdummywebsite.localhost)上,使缓存键与合法 HTTPS 请求碰撞。真实 HTTPS 访客收到的页面会加载攻击者域名的 JavaScript,形成持久化的存储型 XSS。
研究者还测试了 Cloudflare 前置、Nginx 源站的架构:请求携带 Authorization 头可让 Cloudflare 绕过自身边缘缓存、把请求直接透传给源站,若源站仍执行缓存,攻击者便可直接投毒内部源站缓存,绕过 CDN 这一层防护。
影响评估
受影响面覆盖所有使用自定义 proxy_cache_key、且键中含请求头变量的 Nginx 及同类反向代理部署,无需特殊权限,构造请求即可触发。需要强调的是,对最终缓存键做哈希并不能提供任何保护——只要底层字符串存在歧义,碰撞的输入仍会得到相同的哈希值。CPDoS 会让正常页面在缓存周期内持续返回错误;缓存欺骗会让后台面板、含个人信息的页面被匿名读取;存储型 XSS 则可能被用来窃取会话或投放挂马脚本。
修复建议
- 用带分隔符的结构化格式重写缓存键,例如 proxy_cache_key "$scheme|$host|$request_uri|$http_accept",让任意变量组合都无法冒充另一个请求。
- 不要把可被客户端伪造的请求头纳入缓存键;确需纳入时,先做严格的白名单校验。
- 校验 Host 头取值并强制 HTTP 到 HTTPS 跳转,消除协议混淆带来的键碰撞。
- 对管理路径显式关闭缓存(location /admin { proxy_cache off; }),并全面审计现有 proxy_cache_key 指令。