写在前面:合法学习边界
本文讲解 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,就能看懂大多数缓存相关的安全问题。