HIGH cache poisoningbasic auth

Cache Poisoning with Basic Auth

How Cache Poisoning Manifests in Basic Auth

Basic Auth transmits credentials in the Authorization: Basic base64(user:pass) header. Many downstream caches (CDNs, reverse proxies, API gateways) build a cache key from the request line and selected headers, but they often omit the Authorization header from that key. When the application treats the header as the user identifier and returns user‑specific data, the same cached response can be served to multiple users.

An attacker can poison the cache by sending a request that includes a malicious header (e.g., a crafted Host or X-Forwarded-Host value) that influences the cache key, while also providing a valid Basic Auth credential. If the cache ignores the Authorization header, the response generated for the attacker’s credentials is stored under the poisoned key. Later, a victim’s request that matches the poisoned key receives the attacker’s cached response, potentially leaking the attacker’s data or causing the victim to see attacker‑controlled content.

Concrete example in Node.js/Express:

const express = require('express');
const basicAuth = require('basic-auth-connect');

const app = express();

// Simple Basic Auth middleware
app.use(basicAuth(function (user, pass) {
  return user === 'admin' && pass === 's3cr3t';
}));

app.get('/data', (req, res) => {
  // Imagine this endpoint returns user‑specific data
  res.json({ user: req.auth.user, secret: 'value' });
});

app.listen(3000);

If a cache in front of this app does not include Authorization in its cache key, a response for admin:s3cr3t could be served to any other user whose request matches the poisoned key (e.g., via a manipulated Host header).

Basic Auth-Specific Detection

middleBrick detects cache‑poisoning risk in Basic Auth endpoints by checking for two common misconfigurations:

  • Missing or incorrect Vary: Authorization header – without it, caches may treat responses with different credentials as identical.
  • Absence of restrictive caching directives such as Cache-Control: private, no-store or no-cache. When these are missing, a cache is allowed to store and reuse the response.

The scanner also sends a series of probes that vary the Authorization header while keeping other request components constant. If the responses are identical (same status code, headers, and body) despite different credentials, middleBrick flags a potential cache‑collision issue. Additionally, it tests for header injection vectors (e.g., Host, X-Forwarded-Host, X-Host) that can alter the cache key; if injecting such a header changes the cached response without changing the Authorization header, the finding is reported.

Example of a finding middleBrick might return:

{
  "check": "Cache Poisoning via Basic Auth",
  "severity": "medium",
  "description": "Response varies with Authorization header but cache key does not include it. Missing Vary: Authorization header.",
  "remediation": "Add 'Vary: Authorization' header to responses or configure the cache to include the Authorization header in its cache key."
}

This finding maps to the OWASP API Security Top 10 category **API1:2023 – Broken Object Level Authorization** when the cached data is user‑specific, and to **API3:2023 – Excessive Data Exposure** when sensitive data is leaked via the poisoned cache.

Basic Auth-Specific Remediation

Fixing cache‑poisoning in Basic Auth endpoints involves ensuring that the cache treats requests with different credentials as distinct, and that sensitive responses are not cached at all unless explicitly intended.

1. Add the Vary: Authorization header so caches differentiate based on credentials.

app.get('/data', (req, res) => {
  res.set('Vary', 'Authorization');
  res.json({ user: req.auth.user, secret: 'value' });
});

2. Prevent caching of user‑specific data with appropriate Cache-Control directives.

app.get('/data', (req, res) => {
  res.set('Cache-Control', 'private, no-store');
  res.json({ user: req.auth.user, secret: 'value' });
});

3. If using a reverse proxy or CDN, configure its cache key to include the Authorization header. Example for NGINX:

proxy_cache_key "$scheme$request_method$host$request_uri$http_authorization";
add_header Vary Authorization;

4. Avoid reflecting the Authorization header (or its decoded username/password) in error messages, logs, or response bodies, as this can aid attackers in crafting poisoned entries that leak credentials.

5. Regularly scan the endpoint with middleBrick (via the CLI, GitHub Action, or Dashboard) to verify that the VaryCache-Control headers are present and that varying the Authorization header produces distinct responses.

By applying these controls, the Basic Auth flow remains functionally unchanged while eliminating the cache‑poisoning vector that could lead to credential leakage or unauthorized data exposure.

Frequently Asked Questions

Does middleBrick modify my API to fix cache‑poisoning issues?
No. middleBrick only detects and reports findings, providing remediation guidance. It does not alter code, configurations, or cache behavior.
Can I rely on the <code>Vary: Authorization</code> header alone to prevent all cache‑poisoning attacks?
Adding Vary: Authorization ensures that caches differentiate responses by credentials, but you should also restrict caching with appropriate Cache-Control directives (e.g., private, no-store) for endpoints that return user‑specific data to eliminate any chance of unintended caching.