Cache Poisoning in Restify with Hmac Signatures
Cache Poisoning in Restify with Hmac Signatures — how this specific combination creates or exposes the vulnerability
Cache poisoning occurs when an attacker manipulates cached responses so that subsequent users receive malicious or incorrect data. In Restify, when Hmac Signatures are used to authenticate requests but response caching is configured without considering signature variability, the cache can key responses on non-authoritative inputs such as URL path alone. If the same endpoint serves different representations based on an Authorization header containing an Hmac signature, and the cache ignores the signature, one user’s cached response can be served to another user. This can leak user-specific data or cause privilege confusion when role-dependent content is cached without considering the signed request context.
For example, consider an endpoint /api/v1/profile that includes an Hmac signature in a custom header like x-api-signature. If Restify caches the response at the CDN or application layer using only the request path, two different users with different signatures (and therefore different permissions or data) could receive the same cached response. This happens because the signature is stripped or not part of the cache key, and the cache treats the requests as identical. Sensitive information from one user may be inadvertently exposed to another, and an attacker may probe differences in timing or error messages to infer whether a particular resource exists for another user.
The interaction between Hmac Signatures and caching also amplifies risks when query parameters or headers that should influence authorization are omitted from cache keys. If signature generation covers a canonical set of headers and the cached response is served from a key that does not include those headers, the integrity check passes on replay but the authorization context is lost. This can lead to scenarios where a low-privilege cached response is served to a high-privilege request, or where a response containing sensitive data is cached for public consumption. Such misconfigurations map to findings in categories like Authentication and Data Exposure within the scan, highlighting that the unauthentized attack surface includes cache behavior.
Hmac Signatures-Specific Remediation in Restify — concrete code fixes
To remediate cache poisoning when using Hmac Signatures in Restify, ensure the cache key incorporates all inputs that affect authorization and data scope, including signature-related headers or a canonical representation of them. Avoid caching responses where the same path can yield different authorized data unless the cache key explicitly includes the signature or a derived user context. Below are two practical approaches with code examples.
Approach 1: Exclude sensitive endpoints from caching
If an endpoint uses Hmac Signatures to deliver user-specific or role-specific data, configure Restify to not cache those responses. You can conditionally skip caching by checking the presence of the signature header or route pattern.
const restify = require('restify');
const server = restify.createServer();
server.pre((req, res, next) => {
// Skip caching for endpoints that validate Hmac Signatures
if (req.headers['x-api-signature'] && req.path.startsWith('/api/v1/')) {
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');
res.setHeader('Pragma', 'no-cache');
res.setHeader('Expires', '0');
}
return next();
});
server.get('/api/v1/profile', (req, res, next) => {
// Signature validated elsewhere; response is user-specific
res.send({ user: req.user });
return next();
});
server.listen(8080, () => console.log('Server listening on port 8080'));
Approach 2: Include signature-derived or canonical fields in cache key
If caching is required, derive a cache key that includes a hash of the canonical headers used in Hmac verification (e.g., date and request target) or include a user/scope identifier that is part of the signed payload. This ensures that different signed contexts map to different cache entries.
const crypto = require('crypto');
const restify = require('restify');
const server = restify.createServer();
function cacheKeyFromRequest(req) {
// Canonical string built from headers involved in Hmac signing
const canonical = [
req.method.toUpperCase(),
req.path,
req.headers['x-date'] || '',
req.headers['x-api-key'] || ''
].join('|');
const hash = crypto.createHash('sha256').update(canonical).digest('hex');
// Include a user identifier if present after signature validation
return `v1:${hash}:${req.user ? req.user.id : 'anon'}`;
}
server.use((req, res, next) => {
// Example: a caching layer could use cacheKeyFromRequest(req)
const key = cacheKeyFromRequest(req);
// pseudo operation: cachedResponse = cache.get(key);
// if (!cachedResponse) { /* compute and store with key */ }
return next();
});
server.post('/api/v1/data', (req, res, next) => {
// After Hmac signature validation, serve or cache response keyed by user scope
res.send({ processed: true });
return next();
});
server.listen(8080, () => console.log('Server listening on port 8080'));
These examples emphasize that Hmac Signatures change the trust boundary: the cache must respect signature scope. Combine these practices with server-side scan tools to detect misconfigured caching on authenticated endpoints and to validate that cache keys account for authorization context.