Cache Poisoning in Adonisjs with Basic Auth
Cache Poisoning in Adonisjs with Basic Auth — how this specific combination creates or exposes the vulnerability
Cache poisoning occurs when an attacker causes a cache to store malicious content that is then served to other users. In AdonisJS applications that use HTTP Basic Authentication, this risk can emerge when responses are cached based on incomplete or untrusted request attributes. If a cache key omits the Authorization header or normalizes it in a way that strips credentials, an authenticated response for one user might be reused for another user with different permissions.
For example, an endpoint that returns user-specific data might be cached with a key derived only from the request path and query parameters. When a Basic Auth request arrives, the cache layer may treat it as identical to a previous unauthenticated or differently authenticated request and return the cached response. This can leak one user’s data to another, effectively bypassing per-user authorization checks. In AdonisJS, this can happen if caching logic in the application or an upstream proxy/cache does not account for the Authorization header as part of the cache key.
The interaction with Basic Auth is notable because the header is base64-encoded but not encrypted; if intermediaries log or cache the full request—including the Authorization header—they might inadvertently store responses alongside credentials. Even without storing credentials, failing to include the header in the cache key can cause privilege confusion: an admin’s cached response could be served to a non-admin user who happens to hit the same normalized URL. This is a cache poisoning vector because the cached content is not isolated by authentication context, leading to unauthorized data exposure.
To detect this, scans such as those performed by middleBrick examine whether authenticated responses are being cached without incorporating the Authorization header into cache keys. The presence of public or shared caches that do not vary by credentials is a strong indicator. Because Basic Auth sends credentials on every request, any mismatch between the cached key and the actual authorization context can result in incorrect or sensitive content being returned.
Basic Auth-Specific Remediation in Adonisjs — concrete code fixes
To mitigate cache poisoning with Basic Auth in AdonisJS, ensure cache keys explicitly incorporate the Authorization header or a per-user derived value. Avoid caching sensitive responses in shared caches unless cache partitioning by credentials is guaranteed. Below are concrete remediation patterns and examples.
1. Do not cache responses that contain user-specific data
If an endpoint returns data scoped to the authenticated user, prevent it from being stored in shared caches. You can set headers to disallow caching by proxies or the browser cache.
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
export default class UsersController {
public async me({ request, response, auth }: HttpContextContract) {
const user = await auth.authenticate()
// Prevent caching of user-specific responses
response.header('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate')
response.header('Pragma', 'no-cache')
response.header('Expires', '0')
return { id: user.id, username: user.username }
}
}
2. Include credentials in cache key when caching is required
If you must cache per-user data, derive a cache key that includes a user-specific identifier rather than relying on the raw Authorization header. Do not use the full Authorization header as a cache key in logs or caches. Instead, use a stable user ID or username.
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import { Cache } from '@ioc:Adonis/Core/Cache'
export default class ProfilesController {
public async show({ params, auth }: HttpContextContract) {
const user = await auth.authenticate()
// Use a user-specific cache key, not the Authorization header
const key = `profile:${user.id}`
const cached = await Cache.getOrPut(key, async () => {
// Expensive operation, safe because key is user-specific
return computeProfileData(user)
})
return cached
}
}
function computeProfileData(user: any) {
return { preferences: {}, settings: {} }
}
3. Validate and normalize Authorization before caching
When integrating with cache layers that inspect requests, validate and normalize credentials safely. Do not store raw Authorization headers in cache metadata or logs. If you must pass credentials to an internal cache layer, use a token derived from the user identity rather than the Basic Auth token itself.
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
export default class ItemsController {
public async index({ request, response, auth }: HttpContextContract) {
const user = await auth.authenticate()
// Derive a safe cache token from user identity
const cacheToken = `user:${user.id}`
// Use cacheToken in internal cache logic if needed
response.header('X-Cache-Token', cacheToken)
return { items: [] }
}
}
4. Configure reverse proxy or CDN cache rules carefully
If using a reverse proxy or CDN, ensure cache rules vary by Authorization header or by a derived user identifier for authenticated endpoints. Avoid caching authenticated responses in a shared space without explicit partitioning.
# Example CDN/edge configuration concept (not AdonisJS code)
# Cache key expression:
# (protocol://host/path) + "?v=" + query_params + "&user=" + user_id
# Do NOT include raw Authorization header in cache keys.