Cache Poisoning in Restify with Mutual Tls
Cache Poisoning in Restify with Mutual Tls — 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, this typically arises from insufficient input validation and weak cache-key construction, especially when Mutual TLS is used for transport-layer authentication. Mutual TLS ensures that both client and server present valid certificates, but it does not inherently protect application-layer semantics such as request parameters, headers, or cookies that determine cache key uniqueness.
When Restify is configured to cache responses (for example via an external cache or in-memory caching layer), the cache key often includes components like the request URL, selected headers, or query parameters. If the server uses the TLS client certificate information as part of authorization but still includes non-authorization request attributes in the cache key, an attacker who can influence any non-mutual-TLS-controlled attribute may cause different users to share cached responses. For instance, an attacker could craft a request with a poisoned query parameter like redirect_url that is not considered part of the TLS identity but is included in the cache key. The server processes the request over Mutual TLS, caches the response keyed by the poisoned parameter, and later serves that poisoned response to other users who do not have the same parameter, leading to unauthorized data exposure or redirection.
Specific Restify patterns that exacerbate this include dynamically setting cache keys based on headers that are not integrity-protected by TLS, such as x-user-id or x-tenant. If Mutual TLS only validates the client identity but the application logic uses additional headers to differentiate cache entries, an attacker may attempt to manipulate these headers in scenarios where they can inject or guess values. Even with Mutual TLS, if the server does not validate or normalize these inputs before caching, the vulnerability remains. The interaction is subtle: Mutual TLS prevents certain on-path attacks and ensures authenticated connections, but it does not prevent logical flaws in how cache keys are derived from HTTP message contents. Therefore, cache poisoning in this context is about inconsistent security boundaries between transport-layer authentication and application-layer caching logic.
Real-world attack patterns mirror those in the OWASP API Top 10 and common input validation weaknesses. For example, an attacker might send a request with ?url=evil.com over a valid client certificate, causing the server to cache a response that redirects to malicious content. Subsequent requests with different certificates but the same poisoned parameter may receive the malicious redirect if the cache key does not differentiate by certificate or if normalization is inconsistent. This aligns with BOLA/IDOR risks when cache entries are accessed across different authorization contexts. MiddleBrick scans detect such misconfigurations by analyzing OpenAPI specs and runtime behavior, identifying where cache-related headers or parameters are not properly bounded by authorization logic.
Mutual Tls-Specific Remediation in Restify — concrete code fixes
Remediation focuses on ensuring cache keys incorporate Mutual TLS identity and that input validation treats all client-controlled data as untrusted. Do not rely on Mutual TLS alone to enforce authorization for cache segregation; explicitly bind the client certificate subject or a derived identifier to the cache key. Additionally, normalize and validate all headers and query parameters before using them in cache lookups.
Below is a Restify example that demonstrates secure caching with Mutual TLS. The server extracts the client certificate subject, hashes it, and includes it in the cache key. It also validates and normalizes query parameters to prevent poisoning.
const restify = require('restify');
const crypto = require('crypto');
const server = restify.createServer();
// Middleware to extract and hash client certificate subject
function tlsCacheKey(req, res, next) {
const cert = req.connection.getPeerCertificate ? req.connection.getPeerCertificate() : {};
const subject = cert.subject || {};
const subjectStr = [subject.CN, subject.O, subject.OU].filter(Boolean).join('|');
const tlsHash = crypto.createHash('sha256').update(subjectStr).digest('hex');
req.cacheKeyBase = tlsHash;
next();
}
// Middleware to normalize and validate query parameters
function validateAndNormalize(req, res, next) {
if (req.query && req.query.redirect_url) {
try {
const url = new URL(req.query.redirect_url);
if (!['https', 'http'].includes(url.protocol)) {
req.invalidParam = true;
}
} catch (e) {
req.invalidParam = true;
}
}
next();
}
server.pre(tlsCacheKey);
server.pre(validateAndNormalize);
server.get('/api/resource', (req, res, next) => {
if (req.invalidParam) {
return res.send(400, { error: 'Invalid parameter' });
}
const cacheKey = `${req.cacheKeyBase}:${req.url}`;
// pseudo cache lookup using cacheKey
// ensure cache entries are scoped by TLS identity
res.send({ key: cacheKey, data: 'safe' });
return next();
});
server.listen(8080, () => {
console.log('Server listening on port 8080');
});
In this example, tlsCacheKey derives a deterministic hash from the certificate subject, which becomes part of the cache key. This prevents attackers from poisoning the cache using headers or parameters that do not include the TLS identity. The validateAndNormalize middleware ensures that untrusted inputs like redirect_url are validated before any processing, mitigating injection into cache-related logic. For production use, pair this with strict Mutual TLS policies in your Restify TLS options and avoid including unvalidated headers in cache keys.
Additionally, review your caching layer’s behavior: ensure that cached responses are not shared across different TLS identities unless explicitly intended. MiddleBrick’s scans can help identify mismatches between declared cache rules and actual runtime behavior, highlighting where input validation does not account for Mutual TLS boundaries.