Bleichenbacher Attack in Restify with Mutual Tls
Bleichenbacher Attack in Restify with Mutual Tls — how this specific combination creates or exposes the vulnerability
A Bleichenbacher attack targets RSA encryption schemes that use PKCS#1 v1.5 padding and rely on error messages that distinguish between padding errors and other failures. In Restify, when Mutual TLS is configured but application-level encryption or token validation reintroduces such distinguishable error behavior, the combination can amplify information leakage across the secure channel.
Mutual TLS authenticates both client and server, which prevents on-path attackers from injecting arbitrary requests. However, it does not eliminate application-side handling of encrypted or signed tokens (for example, JWTs encrypted with RSAES-PKCS1-v1_5) where padding errors are treated differently from decryption or signature failures. A Restify service that processes such tokens and returns distinct HTTP status codes or response bodies for padding failures versus other errors allows an off-path adversary to iteratively decrypt ciphertext or sign messages without knowing the private key.
Consider a scenario where a Restify endpoint accepts a client certificate for transport security, then decrypts an RSA-encrypted JWT using a public key and checks padding validity. If the server responds with 401 and a message like "invalid padding" for padding errors and 400 with "invalid signature" for other errors, an attacker can mount a Bleichenbacher adaptive chosen-ciphertext attack by sending modified ciphertexts and observing status and body differences. The presence of Mutual TLS does not prevent this; it only ensures the client is known, while the server’s error differentiation provides the oracle required for the attack.
In practice, this manifests when Restify applications integrate third-party libraries or legacy token validation that expose timing or error distinctions. The attack leverages the oracle to gradually recover plaintext or forge tokens, despite the transport security provided by Mutual TLS. Therefore, the vulnerability arises not from the TLS layer but from how the application handles cryptographic operations and error reporting after the TLS handshake completes.
Mutual Tls-Specific Remediation in Restify — concrete code fixes
Remediation focuses on ensuring that error handling for cryptographic operations does not leak distinguishable information, regardless of Mutual TLS being in place. Use constant-time operations for padding checks and return uniform error responses.
Example Restify server with insecure error handling (to avoid):
const restify = require('restify');
const server = restify.createServer();
const tlsOptions = {
cert: '/path/to/server-cert.pem',
key: '/path/to/server-key.pem',
ca: '/path/to/ca.pem',
requestCert: true,
rejectUnauthorized: true,
};
server.use(restify.plugins.tls(tlsOptions));
server.post('/token', (req, res, next) => {
const { encryptedToken } = req.body;
try {
const payload = decryptRsaPkcs1v15(encryptedToken, publicKey); // hypothetical
const token = JSON.parse(payload);
// process token
res.send(200, { ok: true });
} catch (err) {
if (err.message.includes('padding')) {
res.status(400).send({ error: 'invalid padding' });
} else {
res.status(400).send({ error: 'invalid token' });
}
}
return next();
});
server.listen(8080);
The above example is vulnerable because the error message and HTTP status differentiate padding failures. An attacker can use this as an oracle.
Secure version using constant-time comparison and uniform response:
const restify = require('restify');
const server = restify.createServer();
const tlsOptions = {
cert: '/path/to/server-cert.pem',
key: '/path/to/server-key.pem',
ca: '/path/to/ca.pem',
requestCert: true,
rejectUnauthorized: true,
};
server.use(restify.plugins.tls(tlsOptions));
function constantTimeEquals(a, b) {
if (a.length !== b.length) return false;
let result = 0;
for (let i = 0; i < a.length; i++) {
result |= a[i] ^ b[i];
}
return result === 0;
}
server.post('/token', (req, res, next) => {
const { encryptedToken } = req.body;
try {
const payload = decryptRsaPkcs1v15(encryptedToken, publicKey); // hypothetical
const token = JSON.parse(payload);
// process token
res.send(200, { ok: true });
} catch (err) {
// Always return the same status and generic message
res.status(400).send({ error: 'invalid token' });
}
return next();
});
server.listen(8080);
Key remediation steps:
- Use a constant-time decryption or verification routine to avoid timing leaks.
- Ensure error messages do not distinguish between padding failures, signature failures, or malformed ciphertext.
- Apply the same response status and body for all cryptographic failures (e.g., 400 with { error: 'invalid token' }).
- Even with Mutual TLS enforced, validate and sanitize all inputs and cryptographic handling to prevent oracle-based attacks.