导读:本期聚焦于木下创作的《CDN缓存中毒究竟是如何发生的又该怎么有效防御》,敬请观看详情。把未经验证的用户请求头透传到源站,再被CDN原样缓存,是缓存中毒最常见的入口。攻击者借助Host或X-Forwarded-Host等字段注入恶意资源路径,使后续用户命中污染缓存。与单纯XSS不同,中毒影响的是共享缓存层,危害范围覆盖全部访客。理清缓存键构造规则、关闭不必要的头透传、对重定向与脚本响应做内容校验,才能从机制上切断污染链。本文从请求处理原理、典型攻击路径与防护落地三方面拆解实战要点。

CDN缓存中毒是一种针对内容分发网络共享缓存层的攻击方式,攻击者通过操纵某些被CDN忽略但被源站使用的请求参数,让源站返回错误甚至恶意的内容,并被CDN当作合法响应缓存起来。此后所有命中同一缓存键的用户都会收到被污染的数据,造成大范围的页面篡改、脚本注入或跳转劫持。理解这类问题的核心,在于搞清楚CDN的缓存键到底由哪些部分构成,以及源站为何会信任那些不在缓存键中的请求头。

CDN缓存中毒究竟是如何发生的又该怎么有效防御

CDN缓存键与源站处理的错位原理

大多数CDN默认只把请求的URL路径、查询字符串以及少数指定头作为缓存键,也就是说,两份请求只要这些关键部分相同,CDN就认为它们是同一个资源。但源站应用在生成响应时,往往会读取Host、X-Forwarded-Host、X-Original-URL等头部来拼接重定向地址或静态资源链接。这种缓存键与业务参数的错位,正是缓存中毒能够成立的技术根基。

举个简单的例子,源站代码里用请求头中的X-Forwarded-Host来构造登录页的跳转地址,而CDN并没有把这个头纳入缓存键。攻击者在第一次请求时带上一个恶意的X-Forwarded-Host值,源站返回包含恶意域名的响应,CDN因为缓存键没变就将其保存。接下来正常用户访问同样URL时,拿到的就是带恶意跳转的页面。下面这段Node.js示例展示了危险的写法:

const express = require('express');
const app = express();
app.get('/login', (req, res) => {
  // 错误示范:直接使用转发头拼装跳转地址
  const host = req.headers['x-forwarded-host'] || req.hostname;
  res.send('<a href="https://' + host + '/auth">请登录</a>');
});
app.listen(3000);

上面的代码里,一旦CDN不把x-forwarded-host当作缓存键,攻击请求和正常请求的缓存键一致,污染响应就会被复用。要解决这个错位,第一步是梳理出业务里所有基于请求头动态生成响应的位置,确认它们是否落在CDN的缓存键之外。只有把“影响响应的输入”和“决定缓存的输入”对齐,才能谈得上防护。

典型缓存中毒攻击路径与利用方式

最常见的利用路径是借助Host类头部进行开放重定向或前端资源替换。攻击者将X-Forwarded-Host设为恶意站点,源站返回的HTML中引用了https://恶意站点/xxx.js,CDN缓存后,普通用户加载页面时就会拉取攻击者控制的脚本,形成存储型XSS的放大版。由于CDN边缘节点遍布各地,一次污染可能影响数以万计的访客。

另一种路径是利用未纳入缓存键的查询参数或Cookie。某些源站会根据特定Cookie切换页面语言并内联样式,但CDN按URL缓存,导致A语言用户的页面被B语言用户看到,若攻击者注入带钓鱼表单的本地化内容,同样构成中毒。下面是一段PHP中容易被忽视的写法:

<?php
// 根据未参与缓存键的 cookie 输出不同内容
$lang = $_COOKIE['lang'] ?? 'cn';
if ($lang === 'en') {
  echo '<script src="https://evil.com/p.js"></script>';
}
?>

如果CDN只按路径缓存,而Cookie不在键中,这段恶意脚本就可能被缓存并投送给所有访客。实践中,安全团队应当用自动化工具遍历所有请求头与参数,观察哪一些能改变响应体却不改变缓存键。这种差分测试是发现中毒面的有效手段,比单纯看文档更可靠。

还有一类利用方式是配合缓存碎片投毒,攻击者先污染某个被页面包含的片段接口,再让主页面由于引用了该片段而被连带缓存。这种组合攻击在微服务架构下尤其隐蔽,因为边缘缓存规则往往只配置了主域名,对内网接口缺乏约束。

防御CDN缓存中毒的落地措施

最根本的防御是收紧缓存键,把任何会影响源站输出的头与参数都加入CDN的缓存键配置,或者直接在边缘节点丢弃、覆盖不可信的头。例如Cloudflare和主流CDN都支持自定义缓存键,将X-Forwarded-Host排除在信任范围之外,或固定为一个内部域名。这样攻击者即便发送异常头,也不会改变缓存归属。

源站侧也必须消除对外部头的隐式信任。前面Node.js示例应改为从服务端配置读取合法域名,而不是读请求头。下面是修正后的写法:

const express = require('express');
const app = express();
const ALLOWED_HOST = 'www.ipipp.com';
app.get('/login', (req, res) => {
  // 正确示范:使用固定配置而非请求头
  res.send('<a href="https://' + ALLOWED_HOST + '/auth">请登录</a>');
});
app.listen(3000);

此外,应对响应内容做完整性校验,尤其是脚本与样式链接的域名白名单。CDN层面可开启“缓存清除告警”,当同一资源响应体发生明显变化时触发人工审核。对于涉及用户相关内容的接口,应明确标记Cache-Control: private,避免被公共边缘节点缓存。综合来说,缓存中毒的治理不是单点补丁,而是缓存键设计、源站输入校验与监控响应三者协同的工程问题。

CDN缓存中毒Web安全修改时间:2026-08-19 00:52:39

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。