HTTP 缓存与缓存控制

理解 Cache-Control、ETag、Last-Modified 与 304 的协作方式,以及缓存命中、缓存投毒、敏感数据被缓存等风险与防御。

写在前面:合法学习边界

本文讲解 HTTP 缓存的原理与防御。不要利用缓存机制去污染或探测真实系统的缓存内容,必须在授权环境中学习。

一、为什么需要缓存

缓存的价值是让“重复读的东西不用每次都回源”,从而降低延迟和服务器压力。

常见层级:

1
2
3
4
浏览器缓存
CDN 缓存
反向代理缓存
应用层缓存

缓存要解决的核心问题是:什么时候能用旧的,什么时候必须用新的

二、控制缓存的头部

1. Cache-Control

这是最常被用来控制缓存的头:

1
2
3
4
no-store:不缓存(敏感数据)
no-cache:可以缓存,但用之前必须回源校验
max-age=3600:缓存 1 小时
public / private:是否能被共享缓存缓存

2. ETag 与 Last-Modified

用于条件请求:

1
2
请求带 If-None-Match: <etag>
服务器校验后返回 304,复用本地缓存

ETag 比 Last-Modified 更精确,因为它基于内容变化,而不只是时间。

三、缓存的关键场景

一个典型的协商过程:

1
2
浏览器:GET /a.png(带 If-None-Match)
服务器:304 Not Modified(内容没变)

如果是动态且敏感的数据,就不应该被共享缓存缓存:

1
个人资料、订单、凭据相关接口要 no-store 或 private

四、常见风险

1. 敏感数据被缓存

1
2
登录页、个人中心、API 响应被共享缓存缓存
包含 token、身份证、订单的数据泄露给下一个用户

2. 缓存键覆盖(缓存投毒)

缓存以 URL(可能加 Vary)做键。如果缓存键设计不合理,攻击者可能让“毒响应”被缓存,其他人访问到被污染的内容。

1
2
参数、头参与缓存键的规则要清楚
Vary 要覆盖影响内容的关键头

3. 缓存与鉴权冲突

1
2
带鉴权的接口被 CDN 缓存
登出后旧内容仍在缓存里

4. 失效困难

1
2
没有版本号,更新后用户拿到旧资源
max-age 过长,回滚困难

五、防御建议

1
2
3
4
5
敏感接口用 Cache-Control: no-store
动态内容避免被共享缓存命中
明确缓存键与 Vary 规则
资源更新用带版本的 URL
对缓存命中率和异常 304 做监控

六、报告中的正确写法

示例:

1
2
3
发现:个人资料接口响应包含 token,且未设置 Cache-Control,可被共享缓存缓存。
风险:后续用户访问可能读到前一个用户的敏感数据。
建议:该接口设置 Cache-Control: no-store;检查缓存键与 Vary 配置;对缓存内容做清理。

报告要写清验证过程和影响,而不是只写“缓存配置有问题”。

七、小结

缓存是双刃剑:用得好降低延迟,用错就是敏感数据泄露和内容投毒。理解 no-store / no-cache / max-age / ETag / Vary,就能看懂大多数缓存相关的安全问题。