← Back to writing

Technical note · api / cache / web-security

Why X-Cache: HIT in an API is not a vulnerability on its own

Evaluating cache indicators in the context of cross-user data isolation and correct reuse.

Seeing X-Cache: HIT in an API response is a starting point for investigation. A security assessment, however, needs to explain whose response it is and in what context it is being reused. Serving public catalog data from a cache may be expected behavior. Giving a user-specific record to another user is a different problem.

Moreover, X-Cache is not a standardized security verdict; its meaning must be interpreted according to the provider. For example, Fastly may report some synthetic responses and stale responses as HIT as well. In multilayer architectures, the header may contain more than one status. Do not interpret a single word as the behavior of the entire system. Fastly X-Cache reference

Read the headers together

Age expresses the estimated time since the response was generated or successfully validated at the origin. It includes time spent in intermediate caches and network transit times; it is not just a counter of time spent waiting in the last shared cache. A response to a request carrying Authorization is not automatically considered “never cacheable” either. RFC 9111 restricts a shared cache's use of that response for subsequent requests; the conditions of directives that permit this, such as public, s-maxage, or must-revalidate, must be met. RFC 9111, sections 3.5 and 4.2.3

Common directives also need to be distinguished. Unqualified private prevents storage by a shared cache; a private cache may store the response. no-store prohibits storage. Unqualified no-cache requires successful validation before reuse; it does not prohibit storage altogether. Vary makes the specified request headers part of response selection. None of these takes over the application's authorization checks. MDN Cache-Control, RFC 9111, Vary

Consider two fictional accounts

In a scenario used only for educational purposes, suppose account A's response from /v1/users/me/invoices/latest contains ownerMarker: "ALICE-LAB", while account B's normal response contains ownerMarker: "BOB-LAB". The intended behavior is for each account to see its own record. In a configuration where user-specific responses should not be stored, Cache-Control: private, no-store may be preferred; the server must also authorize the correct user.

Now suppose there is a separate misconfiguration: the cache key uses only the path and does not distinguish user contexts. If B calls the same path after A and sees A's marker, there is a concrete result to investigate in terms of data isolation. The same status code or the same body length does not demonstrate this result; ownership in the content and the expected access policy matter.

Even this observation does not, on its own, prove that the error specifically comes from the cache. An origin producing a response for the wrong user, the browser's local cache, or stale interface data must also be distinguished. The MDN HTTP caching guide is useful for distinguishing a browser's private cache from a shared cache.

Before interpreting the finding

In an authorized test environment, look for answers to these questions together:

  • Are each account's independent baseline response and access rights known?
  • How does the result change when the request order is reversed?
  • Is the actual response body being examined, or is it stale data left in the interface?
  • Do the cache layer and origin logs support the same event?
  • Are tenant, user, and representation differences handled correctly in the reuse policy?

The cache key affects this distinction; how headers, cookies, and query parameters are included in the product being used must be examined separately. The CloudFront cache key explanation provides a product-specific example. Adding user information to the key does not automatically resolve every authorization need either.

Ultimately, a good finding does more than show a HIT header: it explains the expected access, the data sharing that actually occurred, and its connection to caching separately. For a deeper understanding of validation, see the validators section of RFC 9110.

← Back to writingmuhammet.saridogan.work@gmail.com