缓存漏洞被利用后,站点会陷入一个很尴尬的循环:攻击者用极低成本穿透缓存层,迫使源站反复计算动态请求,同时配合CC攻击把穿透后的请求量放大,源站资源在短时间内被耗尽。解决思路不能只盯CC拦截,也不能只补缓存漏洞,必须把两条线并成一条防御链。缓存漏洞是“放大器”,CC攻击是“施力点”,只堵一个口子,另一个口子依然能把站点打穿。
先看缓存层最常见的几个真实问题。第一种是缓存键污染,很多站点把Cookie、User-Agent、Accept-Encoding或者URL参数直接拼进缓存键,攻击者只要给每个请求附加不同的无害参数,就能让同一个页面产生无数份缓存副本。缓存命中率瞬间掉到个位数,源站接收到的请求量成倍增加。第二种是缓存未命中风暴,某些动态接口被错误地配置了缓存规则,但实际响应头里没有Cache-Control或者设置了no-store,导致每次请求都回源。第三种更隐蔽,部分CDN或反向代理对HEAD请求、Range请求、跨域预检请求的处理逻辑和GET不一致,攻击者用这些方法探测缓存是否真的生效,找到缺口后集中穿透。
CC攻击配合缓存穿透时,特征很明显:请求URL高度集中在少量动态路径上,请求频率高但单个IP的并发并不夸张,User-Agent和Referer随机化程度高,请求参数带有明显的变化值。如果只按IP频率限制,攻击者用代理池就能绕过;如果只按URL频率限制,正常用户访问同一接口也会被误伤。所以拦截策略必须结合缓存状态来判断,而不是单纯看请求速率。
缓存键规范化是第一步把缓存键限制在真正影响响应内容的字段上。静态资源只保留URL和Accept-Encoding,动态页面只保留URL路径和必要的查询参数,Cookie、User-Agent、Referer一律不进入缓存键。对于确实需要区分用户身份的场景,使用单独的Vary头配合规范化后的身份标识,而不是把整个Cookie字符串塞进去。Nginx配置里可以用proxy_cache_key明确指定缓存键格式,避免默认行为把过多变量拼进去。
# Nginx缓存键规范化示例
proxy_cache_key "$scheme$request_method$host$uri$is_args$args";
# 仅保留必要参数,过滤无用参数
if ($args ~* "^id=\d+$") {
set $cache_key "$scheme$host$uri$id";
}
缓存键规范化之后,攻击者通过随机参数制造缓存副本的难度会大幅上升。但这还不够,因为有些参数虽然不进入缓存键,依然会被传递到源站。下一步要做的是参数白名单,只允许明确的参数进入后端请求,其余参数直接丢弃。这样即使攻击者附加了随机参数,源站看到的请求也是干净的,不会因为参数变化而触发额外的计算逻辑。
缓存命中率监控与未命中告警缓存漏洞往往在攻击发生前就已经存在,只是没有被触发。建立缓存命中率监控,按路径、按域名、按缓存状态码分别统计,命中率低于阈值时立即告警。正常情况下静态资源命中率应该在95%以上,动态接口如果配置了缓存,命中率也不应低于70%。如果某个路径的未命中数量突然上升,说明要么缓存规则失效,要么有人在刻意穿透缓存。这个信号比单纯看流量峰值更早暴露问题。
监控数据要细化到缓存状态码,MISS、BYPASS、EXPIRED、UPDATING分别统计。BYPASS数量异常增长通常意味着请求携带了跳过缓存的头信息,比如Cache-Control: no-cache或者Pragma: no-cache。攻击者会利用这些头信息强制回源,如果源站没有对这类头做过滤,等于把缓存层完全架空。在边缘节点上直接剥离这些请求头,只对合法的内部请求保留绕过缓存的权限。
CC攻击拦截要分层处理第一层在边缘节点做基础过滤,拦截明显异常的请求模式。比如单个IP对同一URL的请求间隔小于100毫秒,或者单个IP在10秒内请求了超过50个不同的动态路径。这些规则不需要太复杂,关键是执行效率要高,不能因为规则计算拖慢正常请求。使用内存哈希表记录IP和URL的访问时间戳,滑动窗口判断频率,超限后返回444或者直接断开连接。
第二层针对缓存穿透后的回源请求做保护。源站前面加一层轻量级的请求队列,对同一缓存键的并发回源请求合并,只放行一个请求到后端,其他请求等待缓存结果。这样即使攻击者制造了大量未命中请求,源站实际接收到的计算请求数量也会被大幅压缩。Nginx的proxy_cache_lock指令可以实现这个效果,开启后同一缓存键只有一个请求回源,其余请求等待缓存填充。
# 开启缓存锁,合并并发回源请求 proxy_cache_lock on; proxy_cache_lock_timeout 5s; proxy_cache_lock_age 10s;
第三层是源站自身的防护。如果源站是PHP或者Java应用,需要在应用层增加请求频率限制和资源消耗控制。对于需要登录的接口,把用户身份和请求频率绑定;对于公开接口,按IP加设备指纹做联合限流。关键点在于限流阈值要结合缓存命中情况动态调整,当缓存命中率下降时自动收紧限流阈值,缓存恢复后放宽。这种联动机制比固定阈值更灵活,能应对攻击强度的波动。
缓存漏洞的主动检测不能等攻击来了才去查缓存配置。定期用自动化脚本模拟不同请求头、不同参数组合,检查响应头中的缓存状态。重点验证几个场景:带随机查询参数时是否命中缓存,带不同Cookie时是否错误地返回了缓存内容,带Range头时是否绕过了缓存,带no-cache头时是否强制回源。这些测试用例应该纳入上线前的检查流程,每次配置变更后自动跑一遍。
另一个容易被忽略的点是缓存过期策略。如果缓存过期时间设置过短,攻击者可以等到缓存过期后立即发起请求,让源站承受周期性的回源压力。把缓存过期时间设置得足够长,同时配合主动刷新机制,在内容更新时通过API触发缓存清除,而不是依赖过期时间。对于热点内容,可以设置更长的过期时间,减少回源频率。
联动防御的实际效果缓存漏洞修复和CC拦截配合后,站点稳定性会有明显提升。攻击者即使找到了某个缓存穿透点,边缘节点的频率限制会先拦截掉一部分请求,缓存锁会合并掉大部分回源请求,源站限流会兜底保护后端。三层防线叠加后,攻击成本大幅上升,攻击者需要投入更多资源才能达到同样的效果,而站点这边只需要消耗很少的资源就能扛住。
实际部署时要注意配置的兼容性。缓存锁和限流规则可能会影响正常的突发流量,比如秒杀活动或者热点事件。需要为这些场景设置白名单或者临时放宽限制,避免防御机制误伤正常业务。同时要保留完整的日志记录,攻击结束后分析日志,找出攻击者利用的具体漏洞点,彻底修复而不是临时封堵。
缓存漏洞和CC攻击的叠加是当前站点面临的高频威胁组合。单独处理其中任何一个问题都只能解决部分症状,只有把缓存层的漏洞修复和请求层的拦截策略结合起来,才能形成完整的防护闭环。缓存键规范化、缓存状态监控、分层限流、缓存锁合并、动态阈值调整,这些措施组合起来,站点的抗攻击能力会有质的提升。
