Cache Poisoning in Flask with Basic Auth
Cache Poisoning in Flask with Basic Auth — how this specific combination creates or exposes the vulnerability
Cache poisoning occurs when an attacker manipulates cached responses so that malicious or incorrect data is served to users. In Flask applications that use HTTP Basic Authentication, a common misconfiguration can cause authenticated responses to be cached and later served to unauthenticated or different authenticated users. This typically happens when caching is applied at the wrong layer (e.g., a CDN or reverse proxy) without including the Authorization header as part of the cache key, or when responses with sensitive data are marked as cacheable.
When Flask routes protected by Basic Auth do not explicitly prevent caching, intermediaries may store the response alongside the associated Authorization header or, more dangerously, strip the header and cache the body. If the cache key does not incorporate the credentials or the Authorization header, a cached response intended for one user might be delivered to another. For example, an authenticated request to /api/account could result in a cached response that includes sensitive account details; subsequent unauthenticated or low-privilege requests to the same URL may receive that cached data, leading to information disclosure.
Flask itself does not add strong cache-control headers by default, so developers must explicitly set headers such as Cache-Control: no-store for authenticated endpoints. Without these controls, any caching layer between the client and Flask may inadvertently store and reuse responses. The risk is compounded when responses contain personal data or tokens, as the cached content may persist beyond the user’s session. This pattern fits within the broader BOLA/IDOR checks performed by middleBrick, which detect endpoints that expose data across users.
An illustrative request flow shows the danger:
- User A authenticates with
Authorization: Basic dXNlcjE6cGFzc3dvcmQxand receives a JSON response cached by a shared proxy. - User B, who is not authenticated or has lower privileges, makes a request to the same URL without credentials.
- The caching layer returns the cached response from User A, exposing User A’s data to User B.
middleBrick’s unauthenticated scan can identify endpoints that lack appropriate cache-control directives and that return sensitive data, flagging the potential for cache poisoning in combination with authentication mechanisms.
Basic Auth-Specific Remediation in Flask — concrete code fixes
To mitigate cache poisoning with Basic Auth in Flask, ensure authenticated responses are not cached by intermediaries and that cache keys account for the Authorization header. The following practices and code examples help secure Flask routes.
Set explicit cache-control headers for authenticated routes
For any route that validates credentials, set headers to prevent storage by shared caches. This ensures that responses are treated as private or non-cacheable.
from flask import Flask, request, make_response
app = Flask(__name__)
@app.route("/api/account")
def account():
auth = request.authorization
if not auth or not check_credentials(auth.username, auth.password):
return {"error": "Unauthorized"}, 401
response = make_response({"user": auth.username, "balance": 12345})
response.headers["Cache-Control"] = "no-store, no-cache, must-revalidate, private"
response.headers["Pragma"] = "no-cache"
response.headers["Expires"] = "0"
return response
def check_credentials(username, password):
# Replace with secure credential verification
return username == "alice" and password == "secret"
Vary responses by Authorization header at the caching layer
If you must use a caching layer, ensure that the cache key includes the Authorization header or a derived scope. Many CDNs and reverse proxies support a “Vary” header; configure them to vary by Authorization to avoid mixing user-specific responses.
# Example configuration concept for a caching proxy (not Flask code)
# Cache-Control: public, max-age=3600
# Vary: Authorization
In practice, avoid caching authenticated responses unless absolutely necessary, and scope caches per user or per token.
Use token-based auth with short lifetimes where feasible
While this is not a Flask code fix, replacing long-lived Basic Auth tokens with short-lived access tokens reduces the window of exposure if a cached response is served incorrectly. Combine this with strict cache-control headers for defense in depth.
Validate and test with automated security checks
Use tools like middleBrick to detect endpoints that may be vulnerable to cache poisoning. The scanner checks for missing cache-control directives on authenticated responses and highlights misconfigurations across the authentication and data exposure checks.