Bleichenbacher Attack with Bearer Tokens
How Bleichenbacher Attack Manifests in Bearer Tokens
The Bleichenbacher attack targets RSA PKCS#1 v1.5 padding oracle vulnerabilities. When an API validates a Bearer Token that is signed with an RSA key using the RS256 algorithm, the verification step often involves decrypting the signature with the public key and checking that the resulting plaintext has the correct PKCS#1 v1.5 structure. If the validation routine leaks information about whether the padding is correct (e.g., by returning different error messages or timing differences), an attacker can iteratively modify the signature and use the oracle to recover the private key or forge a valid signature.
In the context of Bearer Tokens, the vulnerable code path typically looks like this:
const jwt = require('jsonwebtoken');
function verifyToken(token) {
try {
// The library may expose padding errors via thrown exceptions
return jwt.verify(token, publicKey, { algorithms: ['RS256'] });
} catch (err) {
// Different error messages for malformed signature vs. expired token
if (err.message.includes('invalid signature')) {
// This branch can be used as a padding oracle
throw new Error('Invalid signature');
}
throw err;
}
}
An attacker who can observe whether the API responds with "Invalid signature" versus a generic authentication failure can submit specially crafted signatures. Over many queries (typically thousands), they can deduce the plaintext of the encrypted signature and ultimately compute the private key or create a forged token that passes verification.
This issue is not limited to JWT libraries; any custom RSA verification that distinguishes padding failures from other validation errors creates the same oracle. Because Bearer Tokens are frequently used for API authentication, a successful Bleichenbacher attack can lead to privilege escalation, unauthorized data access, or complete bypass of authentication controls.
Bearer Tokens-Specific Detection
Detecting a Bleichenbacher‑type padding oracle in a Bearer Token implementation requires observing whether the server’s response differs based on padding correctness. Manual testing is tedious, but automated scanners can probe the token validation endpoint with a series of modified signatures and measure response differences (status code, error message, or timing).
middleBrick includes this check as part of its "Encryption" and "Authentication" categories. When you submit an API URL, the scanner:
- Extracts any Bearer Token endpoint (e.g.,
/auth/tokenor a protected resource that expectsAuthorization: Bearer <token>). - Generates a set of RSA signatures with deliberate PKCS#1 v1.5 padding errors.
- Sends each token to the endpoint and records the response.
- If the scanner detects a statistically significant difference in responses (e.g., distinct error messages or timing variance), it flags a potential padding oracle.
Example of using the middleBrick CLI to trigger the scan:
# Install the CLI (npm)
npm i -g middlebrick
# Scan an API that expects Bearer Tokens
middlebrick scan https://api.example.com/orders
The resulting report will list a finding under the Encryption category with severity "High", a description such as "Possible RSA PKCS#1 v1.5 padding oracle detected in Bearer Token verification", and remediation guidance. Because middleBrick works unauthenticated and black‑box, it can spot the issue even when the token signing key is not known to the tester.
Bearer Tokens-Specific Remediation
The most reliable fix is to eliminate the padding oracle by using a verification library that provides constant‑time, opaque error handling, or by moving away from RSA PKCS#1 v1.5 altogether. Below are concrete remediation steps for common stacks.
1. Use a JWT library that hides padding errors
In Node.js, the jsonwebtoken library already wraps the underlying RSA verification and throws a generic JsonWebTokenError for any verification failure. Ensure you are not catching and re‑throwing with message details that leak padding information.
const jwt = require('jsonwebtoken');
function verifyToken(token) {
return jwt.verify(token, publicKey, { algorithms: ['RS256'] });
// Any error thrown here is generic; do not inspect err.message
}
// Usage
try {
const payload = verifyToken(req.headers.authorization.split(' ')[1]);
// proceed
} catch (err) {
// Generic 401 response
res.status(401).send('Unauthorized');
}
2. Prefer RSASSA‑PSS or ECDSA algorithms
RSASSA‑PSS (RS*PS) uses a provably secure padding scheme that is not vulnerable to Bleichenbacher attacks. If you control the token issuance, switch to RS256 → PS256 or use an EC key with ES256.
// When creating a token
const token = jwt.sign(payload, privateKey, { algorithm: 'PS256' });
// When verifying
jwt.verify(token, publicKey, { algorithms: ['PS256'] });
3. Constant‑time comparison for custom verification
If you must implement low‑level RSA verification (not recommended), use a constant‑time function to compare the decrypted PKCS#1 v1.5 structure and never branch on the result.
const crypto = require('crypto');
function constantTimeEqual(a, b) {
if (a.length !== b.length) return false;
let diff = 0;
for (let i = 0; i < a.length; i++) {
diff |= a[i] ^ b[i];
}
return diff === 0;
}
// Example usage after RSA decryption
const decrypted = crypto.publicDecrypt({ key: publicKey, padding: crypto.constants.RSA_PKCS1_PADDING }, signature);
const expected = Buffer.from('0001' + 'ff'.repeat(10) + '00' + sha256Digest, 'hex');
if (!constantTimeEqual(decrypted, expected)) {
// Throw generic error
throw new Error('Invalid token');
}
After applying any of these fixes, re‑scan the API with middleBrick to confirm the finding no longer appears. Remember that middleBrick only reports the issue; it does not apply the fix for you.