Web Cache Security Cheat Sheet / Web Cache 安全备忘录¶
Introduction / 介绍¶
HTTP caches improve performance by reusing stored responses in browsers, reverse proxies, content delivery networks (CDNs), and application data stores. A cache becomes a security boundary when it serves a response to a request other than the one that originally produced it. The HTTP caching specification defines the behavior of private and shared caches, while the MDN HTTP caching guide provides practical examples of common directives.
HTTP 缓存通过在浏览器、反向代理、内容分发网络(CDN)和应用数据存储中重用存储的响应来提高性能。当缓存向请求其以外的请求提供服务时,它就变成了一个安全边界。HTTP 缓存规范定义了私有和共享缓存的行为,而MDN HTTP 缓存指南提供了常见指令的实际示例。
This cheat sheet focuses on preventing sensitive data exposure, web cache poisoning, web cache deception, and cross-tenant data leakage. It applies to developers and operators configuring origin applications, reverse proxies, CDNs, and application-level caches.
本备忘录侧重于防止敏感数据泄露、缓存投毒、缓存欺骗和跨租户数据泄露。它适用于配置源应用程序、反向代理、CDN和应用级缓存的开发人员和运维人员。
Key Risks / 主要风险¶
The security considerations in RFC 9111 describe caches as attractive attack targets because one stored response can affect many users. OWASP's cache poisoning overview also explains how a harmful cached response can be distributed to other visitors.
RFC 9111中的安全考虑因素将缓存描述为有吸引力的攻击目标,因为一个存储的响应可能会影响许多用户。OWASP 缓存投毒概述也解释了有害的缓存响应是如何分发给其他访问者的。
- Sensitive response exposure: A shared cache stores a personalized or authenticated response and returns it to another user.
中文:敏感响应泄露:共享缓存存储了个性化或认证的响应,并将其返回给其他用户。 - Web cache poisoning: An attacker-controlled request input changes a response but is excluded from the cache key. The harmful response is then served to other users.
中文:Web 缓存投毒:攻击者控制的请求输入更改了响应,但被排除在缓存键之外。然后,有害的响应被提供给其他用户。 - Web cache deception: A dynamic or personalized URL is made to look like a static asset, causing an intermediary to cache sensitive content.
中文:Web 缓存欺骗:动态或个性化URL被伪装成静态资产,导致中间件缓存敏感内容。 - Cache key confusion: The cache and origin disagree about URL normalization, query parameters, headers, or request routing, causing unrelated requests to share an entry.
中文:缓存键混淆:缓存和源对URL规范化、查询参数、头部或请求路由有不同意见,导致无关请求共享一个条目。 - Cross-tenant leakage: An application cache omits the tenant or authorization context from its key.
中文:跨租户泄露: 应用程序缓存未从其键中排除租户或授权上下文。 - Stale security state: Cached content remains accessible after permissions, account state, or the underlying resource changes.
中文:过时的安全状态: 在权限、账户状态或底层资源更改后,缓存内容仍然可访问。
Best Practices / 最佳实践¶
Treat cacheability and cache-key design as part of the application's authorization model. RFC 9111 specifies how response directives, request inputs, and validation control whether a stored response can be reused.
将缓存性和缓存键设计视为应用程序授权模型的一部分。RFC 9111 规定了响应指令、请求输入和验证控制如何确定存储的响应是否可以重用。
1. Classify Responses Before Caching / 1. 在缓存之前对响应进行分类¶
Define an explicit policy for every route. Default to preventing shared caching for authenticated, personalized, tenant-specific, or otherwise sensitive responses.
为每个路由定义一个明确的策略。默认情况下,防止共享缓存用于经过身份验证的、个性化的、租户特定的或其他敏感的响应。
- Use
Cache-Control: no-storewhen a response must not be stored by any compliant cache.
中文:当响应必须由任何符合规范的缓存存储时,使用Cache-Control: no-store。 - Use
Cache-Control: privatewhen a response may be stored by a user's private cache but not by a shared cache.
中文:当响应可能由用户的私有缓存存储但不应由共享缓存存储时,使用Cache-Control: private。 - Remember that
no-cachepermits storage but requires successful validation before reuse. It does not mean "do not store."
中文:记住,no-cache允许存储,但在重用之前需要成功验证。它并不意味着“不要存储”。 - Do not assume that cookies make a response private. A
Set-Cookieheader alone does not prevent caching.
中文:不要假设cookie使响应变得私密。仅Set-Cookie头部并不能防止缓存。 - Do not mark a response influenced by
Authorizationor session cookies aspublic, or give it ans-maxage, unless sharing the response has been deliberately designed and reviewed.
中文:不要将受Authorization或会话cookie影响的响应标记为public,或者给它设置s-maxage,除非响应共享已被故意设计和审查。 - Purge previously stored entries when changing a route to
no-store; the directive does not delete old entries.
中文:当更改路由到no-store时,清除之前存储的条目;该指令不会删除旧条目。
For a response containing sensitive data:
对于包含敏感数据的响应:
Cache-Control: no-store
For non-sensitive content that may be stored only in a private cache and must be revalidated:
对于可能仅存储在私有缓存中且必须重新验证的非敏感内容:
Cache-Control: private, no-cache
2. Design a Complete Cache Key / 2. 设计完整的缓存键¶
Every request input that can change a cacheable response must either be represented in the cache key or be rejected for that route.
每个可以改变缓存响应的请求输入必须要么在缓存键中表示,要么拒绝该路由。
- Include the request method, scheme, host, normalized path, and all relevant query parameters.
中文:包括请求方法、方案、主机、标准化路径以及所有相关查询参数。 - Use
Varyfor representation-selecting headers such asAccept-EncodingorAccept-Language.
中文:使用Vary头部 来选择表示,例如Accept-Encoding或Accept-Language。 - Do not use
Vary: Cookieas a general authorization boundary. Prefer disabling shared caching for personalized responses.
中文:不要将Vary: Cookie用作通用授权边界。优先禁用个性化响应的共享缓存。 - Derive tenant and user identifiers from trusted, authenticated server-side context, not directly from an untrusted header or query parameter.
中文:从受信任的、经过身份验证的服务器端上下文中派生租户和用户标识,而不是直接从不受信任的头部或查询参数中获取。 - Apply the same URL normalization and parameter handling at the CDN, reverse proxy, and origin.
中文:在CDN、反向代理和源端应用相同的URL归一化和参数处理。 - Reject ambiguous requests, including conflicting host information, duplicate parameters with inconsistent meanings, and malformed path encodings.
中文:拒绝模糊请求,包括冲突的主机信息、具有不一致含义的重复参数以及路径编码错误。
For an application-level cache, use a structured, versioned key:
对于应用级缓存,使用结构化、版本化的键:
<environment>:<resource-version>:<tenant-id>:<resource-type>:<resource-id>
Do not put session tokens, API keys, or other secrets in cache keys because keys often appear in logs and administrative interfaces.
不要将会话令牌、API密钥或其他秘密放入缓存键中,因为键通常出现在日志和管理界面中。
3. Prevent Web Cache Poisoning / 3. 防止Web缓存投毒¶
Cloudflare's cache poisoning guidance) (Cloudflare的缓存投毒指南) describes the central design rule: untrusted request inputs that are not part of the cache key must not influence a cacheable response.
- Cache only routes that are explicitly intended to be shared.
中文:仅缓存明确打算共享的路由。 - Do not let unkeyed headers or a body on a
GETrequest alter a cacheable response.
中文:不允许未加键的头部或GET请求的正文更改可缓存的响应。 - Trust
ForwardedandX-Forwarded-*headers only when they were added or replaced by a trusted proxy.
中文:仅在它们被可信代理添加或替换时,才信任Forwarded和X-Forwarded-*头部。 - Canonicalize the host and scheme before using them to generate links, redirects, or security-sensitive headers.
中文:在使用之前,对主机和方案进行规范化,以生成链接、重定向或安全敏感的头部。 - Do not reflect unvalidated request metadata into cached HTML, JSON, redirects, or response headers.
中文:不要将未经验证的请求元数据反射到缓存的HTML、JSON、重定向或响应头部中。 - Separate static assets and dynamic application routes by hostname or an unambiguous path namespace where practical.
中文:在可行的情况下,通过主机名或明确的路径命名空间将静态资源和动态应用路由分开。 - If poisoning occurs, purge every affected cache layer and fix the key or origin behavior before re-enabling caching.
中文:如果发生中毒,清除每个受影响的缓存层,并在重新启用缓存之前修复密钥或源行为。
4. Prevent Web Cache Deception / 4. 防止Web缓存欺骗¶
Web cache deception occurs when an attacker causes a shared cache to store personalized content under a URL that appears cacheable. Cache Deception Armor documents one mitigation: verifying that the requested file extension agrees with the response Content-Type.
当攻击者导致共享缓存存储在看似可缓存的 URL 下的个性化内容时,就会发生 Web 缓存欺骗。缓存欺骗装甲 记录了一种缓解措施:验证请求的文件扩展名是否与响应 Content-Type 一致。
- Base cache eligibility on an allowlisted route and response policy, not only on a file extension.
中文:基于允许列表路由和响应策略确定基本缓存资格,而不仅仅是文件扩展名。 - Make dynamic routes reject unexpected path segments and static-looking suffixes instead of silently routing them to the same handler.
中文:使动态路由拒绝意外的路径段和静态外观后缀,而不是静默地将它们路由到同一处理程序。 - Ensure that a request such as
/account/profile/image.csscannot resolve to the same personalized handler as/account/profile.
中文:确保类似/account/profile/image.css的请求不能解析到与/account/profile相同的个性化处理器。 - Verify that the URL extension and response
Content-Typeagree before caching a static asset.
中文:在缓存静态资产之前,验证 URL 扩展名和响应Content-Type是否一致。 - Ignore or remove query parameters from a cache key only after proving that they cannot change routing, authorization, or response content.
中文:仅在证明查询参数不会更改路由、授权或响应内容之后,才忽略或从缓存键中删除它们。
5. Coordinate Origin and Intermediary Policies / 5. 协调源和中间件策略¶
A secure origin policy can be weakened by a CDN rule, reverse proxy override, or framework default. MDN's caching guide recommends explicitly controlling caching rather than relying on heuristic behavior.
安全源策略可能会因 CDN 规则、反向代理覆盖或框架默认设置而减弱。MDN 的缓存指南 建议明确控制缓存,而不是依赖启发式行为。
- Define browser and shared-cache lifetimes independently with the appropriate directives.
中文:使用适当的指令独立定义浏览器和共享缓存的生存时间。 - Review CDN rules that override origin
Cache-Controlheaders or cache responses by status code, path pattern, or file extension.
中文:审查覆盖源Cache-Control头或根据状态码、路径模式或文件扩展名缓存响应的 CDN 规则。 - Give error pages, redirects, and authentication responses explicit cache policies.
中文:为错误页面、重定向和身份验证响应提供明确的缓存策略。 - Use consistent URL normalization at every layer. When a CDN normalizes its cache key, send the normalized form to the origin as well.
中文:在每个层级上使用一致的 URL 规范化。当 CDN 规范化其缓存键时,也将规范化后的形式发送到源站。 - Maintain a tested purge mechanism for individual keys, tags, or narrowly scoped route groups.
中文:维护针对单个键、标签或范围较窄的路由组的测试过的清除机制。 - Fail closed when a sensitive route has a missing, malformed, or conflicting cache policy.
中文:当敏感路由缺少、格式错误或存在冲突的缓存策略时,应采取关闭失败策略。
6. Protect Application-Level Caches / 6. 保护应用级缓存¶
In-memory, database, and distributed caches can leak data even when HTTP caching is disabled. Authorization must still be enforced on every request before cached data is returned. See the Authorization Cheat Sheet and Multi-Tenant Security Cheat Sheet for related controls.
内存、数据库和分布式缓存即使在禁用 HTTP 缓存的情况下也可能泄露数据。在返回缓存数据之前,必须在每个请求上强制执行授权。请参阅授权速查表和多租户安全速查表以获取相关控制信息。
- Namespace keys by environment and application to prevent collisions.
中文:通过环境和应用程序对命名空间键进行命名,以防止冲突。 - Include tenant identity and every authorization-relevant resource dimension in the key.
中文:在密钥中包含租户身份和每个与授权相关的资源维度。 - Do not let a cache hit bypass object-level or tenant-level authorization.
中文:不要让缓存命中绕过对象级或租户级授权。 - Avoid caching final authorization decisions unless the key includes the complete subject, object, action, policy version, and a tightly bounded lifetime.
中文:除非密钥包括完整的主题、对象、操作、策略版本和紧密限定的生命周期,否则避免缓存最终授权决策。 - Version key formats so schema or permission-model changes cannot reuse incompatible entries.
中文:对版本密钥格式进行版本控制,以便架构或权限模型更改不能重用不兼容条目。 - Restrict administrative access to cache contents, configuration, statistics, and purge operations.
中文:限制对缓存内容、配置、统计信息和清除操作的行政访问。
7. Test the Complete Cache Path / 7. 测试完整的缓存路径¶
Test through the same CDN and proxy path used in production. The Cloudflare cache-key documentation illustrates how scheme, host, path, query parameters, headers, and cookies can contribute to a cache key.
通过在生产环境中使用的相同CDN和代理路径进行测试。Cloudflare缓存密钥文档(https://developers.cloudflare.com/cache/how-to/cache-keys/)说明了方案、主机、路径、查询参数、头和cookie如何有助于缓存密钥。
- Request the same URL as two different users and tenants; neither response may contain the other's data.
中文:以两个不同用户和租户请求相同的URL;任何响应都不应包含另一个的数据。 - Change one header, query parameter, cookie, or path component at a time and verify the expected cache behavior.
中文:每次更改一个标题、查询参数、cookie或路径组件,并验证预期的缓存行为。 - Append static-looking suffixes and unexpected path segments to authenticated routes and verify that they are rejected and not cached.
中文:将静态外观的后缀和意外的路径段附加到已认证的路由上,并验证它们被拒绝且未被缓存。 - Test authorization changes, logout, content updates, and purge operations against already stored entries.
中文:测试授权更改、注销、内容更新和清除操作对已存储条目的影响。 - Monitor for unexpected cache hits on authenticated routes, abrupt hit-ratio changes, unusual forwarded headers, and repeated purge activity.
中文:监控已认证路由上的意外缓存命中、突发的命中率变化、不寻常的转发标题和重复的清除活动。 - Log enough cache-status and routing metadata to investigate incidents, but never log secrets or full sensitive response bodies.
中文:记录足够的缓存状态和路由元数据以调查事件,但绝不要记录机密信息或完整的敏感响应体。
Do's and Don'ts / 该做什么和不该做什么¶
Use these checks alongside the HTTP caching requirements in RFC 9111 and the application's normal authorization review.
将这些检查与RFC 9111中的HTTP缓存要求以及应用程序的正常授权审查一起使用。
Do:
- Set an explicit
Cache-Controlpolicy on every security-relevant response.
中文:在每个与安全相关的响应上设置显式的Cache-Control策略。 - Verify that every response-changing input is included in the cache key or rejected.
中文:验证每个更改响应的输入是否包含在缓存键中或被拒绝。 - Keep cache and origin routing rules consistent.
中文:保持缓存和源路由规则的一致性。 - Authorize the current request before returning cached application data.
中文:在返回缓存的应用数据之前授权当前请求。 - Test with multiple identities and tenants through the production caching path.
中文:通过生产缓存路径使用多个身份和租户进行测试。 - Plan and test targeted cache invalidation before an incident occurs.
中文:在事件发生之前计划和测试针对性的缓存失效。
Don't:
- Assume that authentication, cookies, TLS, or
Set-Cookieautomatically prevents caching.
中文:假设身份验证、cookies、TLS 或Set-Cookie自动防止缓存。 - Cache every URL with a static-looking extension.
中文:缓存每个具有静态外观扩展名的 URL。 - Allow unkeyed request metadata to influence shared responses.
中文:允许未加键的请求元数据影响共享响应。 - Treat
Vary: Cookieas a substitute for an authorization design.
中文:将Vary: Cookie视为授权设计的替代品。 - Include credentials or session identifiers in keys, logs, or purge URLs.
中文:在密钥、日志或清除URL中包含凭据或会话标识符。 - Assume that purging a poisoned entry fixes the underlying vulnerability.
中文:假设清除受毒条目可以修复根本漏洞。
References / 参考¶
- RFC 9111: HTTP Caching) (RFC 9111:HTTP缓存)
- MDN: HTTP Caching) (MDN:HTTP缓存)
- Cloudflare: Avoid Web Cache Poisoning) (Cloudflare:避免Web缓存中毒)