Padding Oracle in Adonisjs with Jwt Tokens
Padding Oracle in Adonisjs with Jwt Tokens — how this specific combination creates or exposes the vulnerability
A padding oracle arises when an application reveals whether a decrypted ciphertext has valid padding, enabling an attacker to recover plaintext or forge tokens without knowing the key. In AdonisJS, this typically occurs when JWT tokens are encrypted (e.g., using jwt.encrypt) but the application exposes error distinctions during verification. AdonisJS relies on the underlying jsonwebtoken library; if verification is implemented with a try/catch that treats invalid padding as a distinct, informative error, an attacker can iteratively submit modified tokens and observe different responses to learn about the plaintext.
Consider an endpoint that accepts an encrypted JWT in an Authorization: Bearer header and decrypts it with a symmetric key. If AdonisJS returns 401 with a message like “invalid padding” for malformed padding and a generic “invalid signature” for other failures, the difference becomes an oracle. Attackers can adapt the Cipher Block Chaining (CBC) ciphertext byte-by-byte, using the oracle’s responses to strip padding correctly and recover the payload. Even when using AES-GCM, misuse such as nonce reuse or treating a decryption error as an authentication bypass can expose behavior that assists an attacker, though GCM does not use padding in the same way as CBC.
In AdonisJS, routes that parse JWTs without uniform error handling are vulnerable. For example, a route that calls jwt.verify and then branches logic based on specific error types can leak information. The framework does not prescribe a particular JWT implementation, so developers must ensure that verification either succeeds or fails with a single, opaque response. This is especially important when tokens carry sensitive data or act as a bearer credential; a padding oracle can lead to token impersonation or information disclosure.
OpenAPI/Swagger analysis can surface endpoints that accept JWTs but do not enforce strict validation, allowing scanners to probe error messages for timing or content differences. A black-box scan might submit tokens with manipulated ciphertexts and measure response codes and bodies to detect non-uniform error handling. Because the scan tests the unauthenticated attack surface, it can identify whether an endpoint distinguishes between padding failures and other errors, flagging the behavior as a high-severity finding.
Compliance mappings are relevant here: findings may align with OWASP API Top 10 “Broken Object Level Authorization” and “Security Misconfiguration”, PCI-DSS requirements on cryptographic integrity, and SOC2 controls around access management. Remediation focuses on making error handling consistent and ensuring cryptographic operations do not expose side channels.
Jwt Tokens-Specific Remediation in Adonisjs — concrete code fixes
To prevent padding oracle issues, standardize verification outcomes and avoid leaking details about token structure. In AdonisJS, use a single try/catch around jwt.verify and return a uniform error response. Never branch on specific token errors, and ensure all verification failures result in the same HTTP status and message.
Example of vulnerable code that can act as an oracle:
import { jwt } from '@adonisjs/auth';
export async function verifyToken(ctx) {
const token = ctx.request.header()['authorization']?.replace('Bearer ', '');
try {
const payload = await jwt.verify(token, ctx.authKeys.publicKey, { algorithm: 'RS256' });
ctx.response.json(payload);
} catch (error) {
if (error.name === 'JsonWebTokenError' && error.message.includes('padding')) {
ctx.response.status(400).json({ error: 'invalid padding' });
} else {
ctx.response.status(401).json({ error: 'invalid signature' });
}
}
}
The above distinguishes padding errors, creating an oracle. Fix it by using a single catch block and a generic message:
import { jwt } from '@adonisjs/auth';
export async function verifyToken(ctx) {
const token = ctx.request.header()['authorization']?.replace('Bearer ', '');
try {
const payload = await jwt.verify(token, ctx.authKeys.publicKey, { algorithm: 'RS256' });
ctx.response.json(payload);
} catch {
ctx.response.status(401).json({ error: 'invalid token' });
}
}
For encrypted JWTs using jwt.encrypt, ensure the key size and algorithm match the library expectations. Use strong algorithms like RS256 or ES256 with proper key management, and avoid custom parsing of the token structure. The following demonstrates encryption and safe verification:
import { jwt } from '@adonisjs/auth';
const token = jwt.sign({ sub: userId }, ctx.authKeys.privateKey, { algorithm: 'RS256', expiresIn: '1h' });
// Transmit token to client
// Verification in a route or middleware
export async function authenticate(ctx) {
const token = ctx.request.header()['authorization']?.replace('Bearer ', '');
if (!token) {
ctx.response.status(401).json({ error: 'authorization header required' });
return;
}
try {
const payload = await jwt.verify(token, ctx.authKeys.publicKey, { algorithms: ['RS256'] });
ctx.auth.user = payload;
} catch {
ctx.response.status(401).json({ error: 'invalid token' });
}
}
Additional measures include rotating keys via JWKS when feasible, enforcing HTTPS to prevent token interception, and validating claims such as iss and aud. Middleware that normalizes errors and uses constant-time comparison where relevant further reduces risk. Regular scans with tools that include LLM security checks can help detect configuration issues that may otherwise aid an attacker.