Padding Oracle in Express with Bearer Tokens
Padding Oracle in Express with Bearer Tokens — how this specific combination creates or exposes the vulnerability
A padding oracle in an Express API that uses Bearer tokens typically arises when encrypted tokens (e.g., JWTs) are processed in a way that leaks information about token validity through timing differences or error messages. In this stack, the token is often encrypted (e.g., using AES) or signed, and if the server performs byte-by-byte comparisons or triggers distinct errors during padding removal, an attacker can infer whether a ciphertext decrypts to valid padding. This enables a padding oracle attack that can recover the plaintext or forge tokens without knowing the secret key.
Consider an Express route that expects an encrypted Bearer token in the Authorization header:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWxpY2UifQ.5F9d7e3Xq...
If the server decrypts the token and checks padding before validating structure, an attacker who can send modified tokens and observe responses (e.g., 401 vs 400, or timing differences) can treat the server as an oracle. This is especially risky when tokens contain sensitive claims or when error handling reveals whether padding was correct. Insecure implementations might use custom decryption logic or non-constant-time verification, which is common when developers integrate crypto libraries directly in Express middleware.
The risk is compounded when the token format is opaque to the application, and the server relies on a library that does not use verified, constant-time padding removal. An attacker can iteratively modify ciphertext blocks and use the oracle’s responses to decrypt or tamper with the token, escalating to privilege escalation or impersonation. This maps to common weaknesses such as CWE-327 (Use of a Broken or Risky Cryptographic Algorithm) and aligns with OWASP API Security Top 10 issues around broken object level authorization and cryptographic failures.
Bearer Tokens-Specific Remediation in Express — concrete code fixes
To mitigate padding oracle risks with Bearer tokens in Express, prioritize using well-audited libraries and constant-time operations. Avoid manual decryption or padding checks; rely on standard JWT libraries that handle verification securely. Below are concrete code examples for secure token validation.
Secure JWT verification with Bearer tokens
Use a maintained library like jsonwebtoken and enforce algorithms explicitly. Never accept unsigned tokens or allow algorithm confusion.
const express = require('express');
const jwt = require('jsonwebtoken');
const app = express();
const PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...\n-----END PUBLIC KEY-----`; // RSA public key for verification
app.use((req, res, next) => {
const auth = req.headers.authorization;
if (!auth || !auth.startsWith('Bearer ')) {
return res.status(401).json({ error: 'Unauthorized' });
}
const token = auth.substring(7);
try {
// Enforce expected algorithm and use public key verification
const decoded = jwt.verify(token, PUBLIC_KEY, { algorithms: ['RS256'] });
req.user = decoded;
next();
} catch (err) {
// Use a generic error to avoid leaking token validity details
return res.status(401).json({ error: 'Invalid token' });
}
});
app.get('/profile', (req, res) => {
res.json({ user: req.user });
});
app.listen(3000, () => console.log('Server running on port 3000'));
Symmetric secret example with consistent error handling
If using HMAC (e.g., HS256), keep the secret strong and avoid dynamic secrets. Always use the library’s verify method and do not attempt to validate padding manually.
const express = require('express');
const jwt = require('jsonwebtoken');
const app = express();
const SECRET = 'super-strong-secret-at-least-256-bit';
app.use((req, res, next) => {
const auth = req.headers.authorization;
if (!auth || !auth.startsWith('Bearer ')) {
return res.status(401).json({ error: 'Unauthorized' });
}
const token = auth.substring(7);
try {
// HS256 verification with consistent error handling
const decoded = jwt.verify(token, SECRET, { algorithms: ['HS256'] });
req.user = decoded;
next();
} catch (err) {
// Generic response to prevent oracle behavior
return res.status(401).json({ error: 'Invalid token' });
}
});
app.get('/data', (req, res) => {
res.json({ items: ['item1', 'item2'] });
});
app.listen(3000, () => console.log('Server running on port 3000'));
Additionally, scanning your Express endpoints with tools like middleBrick can help identify such cryptographic issues and other API security misconfigurations. Using the CLI (e.g., middlebrick scan <url>) or the GitHub Action to fail builds on poor scores can catch these risks early. The MCP Server allows AI coding assistants to trigger scans from your IDE, supporting rapid feedback during development.