Cache Poisoning in Strapi with Basic Auth
Cache Poisoning in Strapi with Basic Auth — how this specific combination creates or exposes the vulnerability
Cache poisoning occurs when an attacker manipulates a cached response so that subsequent users receive malicious or incorrect data. In Strapi, this can arise when responses are cached at a reverse proxy or CDN and vary only by non-authoritative inputs such as headers that are not properly considered for cache key construction. When Basic Auth is used, the Authorization header is typically sent with every request. If caching rules treat requests with an Authorization header as cacheable without validating that the cached response is appropriate for the authenticated user, one user’s authenticated response might be served to another user or to unauthenticated observers who happen to match the cache key.
In Strapi, the admin API and publicly facing endpoints may both be subject to caching if deployed behind a caching layer. Basic Auth credentials can be sent in the Authorization header as Basic base64(username:password). Because the header is often hashed into the cache key, separate cache entries are created per user. However, if the cache key ignores parts of the request that change authorization semantics (for example, an attacker can control a header that is included in the key but does not enforce authorization), or if a response is cached on the first authenticated request and later served to a different authenticated user or to unauthenticated traffic, sensitive information can be leaked.
Consider an endpoint that returns user-specific data and is inadvertently cached with a key that incorporates the Authorization header but does not enforce that the cached response is only used for the same user. An authenticated user could make a request with their credentials; the response is cached. If the caching layer inadvertently allows another user or an unauthenticated request to reuse that cached entry (e.g., due to misconfigured cache rules or Vary handling), the second user may see the first user’s data. Additionally, if Strapi’s public-facing APIs cache responses with user-specific Authorization headers without proper Vary directives, intermediaries may incorrectly serve private data to other clients.
To detect this using schema analysis, an OpenAPI/Swagger spec for Strapi (2.0, 3.0, or 3.1) can be scanned with full $ref resolution to compare runtime behavior against documented authentication. When Basic Auth is used, the scanner checks whether responses that include Authorization are cached or whether cache-related headers like Cache-Control and Vary are missing or inconsistent. Findings may highlight missing Vary headers on authenticated endpoints or caching of status codes that should not be cached (e.g., 200 responses containing sensitive data). These findings are mapped to relevant frameworks such as OWASP API Top 10 and PCI-DSS to prioritize remediation.
Basic Auth-Specific Remediation in Strapi — concrete code fixes
Remediation focuses on ensuring that cached responses are never shared across users and that Authorization headers are handled in a way that prevents unintended cache reuse. Strapi does not directly manage HTTP caching headers when behind a proxy or CDN, so configuration must be applied at the caching layer and in Strapi’s response headers. Use Vary headers to ensure that caches differentiate responses by the full set of authorization and user-context identifiers.
First, configure your reverse proxy or CDN to use a cache key that includes the Authorization header when sensitive endpoints are involved, or avoid caching authenticated responses entirely. For public endpoints that do not require authentication, ensure that no Authorization header is present in the request when caching.
Second, explicitly set response headers in Strapi to control caching behavior. For authenticated user data, use Cache-Control: no-store or Cache-Control: private to prevent shared caches from storing responses. For public data that can be cached, use Vary: Authorization if you must cache authenticated responses, but prefer not to cache authenticated responses at all.
Example Strapi middleware to set security headers on responses:
module.exports = {
async configureMiddlewares(strapi) {
return [
async (ctx, next) => {
await next();
// Apply to all responses
ctx.set('Cache-Control', 'no-store');
ctx.set('Vary', 'Authorization');
},
];
},
};
Example of correct Basic Auth request and response headers:
GET /api/users/me HTTP/1.1
Host: api.example.com
Authorization: Basic dXNlcjpwYXNzd29yZA==
Accept: application/json
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
Vary: Authorization
Date: Wed, 01 Jan 2025 12:00:00 GMT
If you must allow caching for public endpoints, ensure that the Authorization header is omitted from those requests and that Vary is set appropriately. Do not rely on the presence of Basic Auth to enforce authorization; always validate scopes and permissions server-side in Strapi.
Additional remediation includes reviewing any custom controllers or services in Strapi to ensure they do not inadvertently include sensitive data in responses that could be cached. Combine these header strategies with regular scans using tools that check for missing Vary headers and improper caching of authenticated responses to reduce the risk of cache poisoning.