Skip to content

@cache stores responses marked private/no-store, with Set-Cookie, or for Authorization requests #296

Description

@allen0099

Problem

@cache stores a response in the shared backend even when the handler has marked it as not shareable, and then overwrites that marking:

@app.get("/me")
@cache(ttl=60)
async def me(request: Request, response: Response):
    response.headers["Cache-Control"] = "private, no-store"
    return {"user": request.headers.get("authorization")}
GET /me  Authorization: Bearer alice  -> {"user": "Bearer alice"}
GET /me  Authorization: Bearer bob    -> {"user": "Bearer alice"}   Cache-Control: max-age=60

_cacheable_headers() (cache.py) drops cache-control, the entry is written anyway, and _with_cache_control() replaces the handler's header with the decorator's.

The same happens for two other signals that a response belongs to one caller:

  • The request carries Authorization. RFC 9111 §3.5 forbids a shared cache from reusing such a response unless it is explicitly public, s-maxage or must-revalidate.
  • The response sets a cookie (Set-Cookie is stripped from the entry, but the body it came with is stored and replayed to everyone).

The docs warn about authenticated endpoints (HTTP_CACHING.md, "Authenticated endpoints"), but nothing at runtime catches the mistake, and the result is one user's data served to another.

Proposal

On a miss, do not write the entry (and serve the response as the handler built it) when:

  • the handler's response has Cache-Control containing private or no-store; keep the handler's header instead of overwriting it;
  • the response has Set-Cookie;
  • the request has Authorization, unless the route is public=True (matching RFC 9111 §3.5) or a new opt-in such as cache_authorized=True is set (for per-user key builders).

Log each skip at DEBUG. Routes that do none of these are unaffected.

Related: #233 (header replay), #77 / #268 (Vary-aware keys).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinghttp-cacheThe @cache decorator, cache keys and Cache-Control handlingsecuritySecurity vulnerability or hardening

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions