Denial Of Service in Adonisjs with Jwt Tokens
Denial Of Service in Adonisjs with Jwt Tokens
AdonisJS applications that rely exclusively on JWT tokens for authentication can be susceptible to denial of service (DoS) when token validation logic is resource-intensive or not resilient under high request volume. A DoS scenario arises when an endpoint that verifies JWT tokens does not enforce strict input constraints, rate limits, or short token lifetimes, allowing an attacker to trigger repeated, costly validation operations. For example, if your API accepts bearer tokens on every request and each verification involves synchronous decoding, signature checking, and claims validation without caching or early rejection of obviously malformed tokens, an attacker can send many requests with valid-looking but expensive-to-verify tokens, consuming CPU cycles and potentially saturating event loop resources in a Node.js runtime.
Another vector specific to AdonisJS with JWT is misconfigured token expiry and refresh workflows. If refresh tokens are long-lived or if the application issues new access tokens without strict quotas, an authenticated attacker who compromises a single token can amplify the impact by generating many short-lived tokens through repeated refresh requests. This can manifest as token churn that stresses your token introspection or database lookups (if you use a token blacklist or user session store). Additionally, if your application resolves dynamic route parameters that influence token handling (e.g., tenant IDs in subdomains or paths) without proper validation, an attacker can probe for path or parameter combinations that trigger expensive lookups or regex operations in your authentication middleware, effectively weaponizing token validation logic to degrade service.
In practice, an unauthenticated attack surface that includes JWT verification endpoints can be probed using malformed tokens, oversized claims, or deeply nested payloads that exploit parser inefficiencies. AdonisJS applications that wire authentication guards globally and do not exclude public endpoints from token checks may inadvertently force every request through cryptographic verification steps, increasing latency under load. The combination of these factors — reliance on JWT, lack of rate limiting on authentication paths, and absence of early request filtering — creates a DoS vulnerability where legitimate users experience slow responses or timeouts while the server struggles to process abusive token validation work.
Jwt Tokens-Specific Remediation in Adonisjs
To mitigate DoS risks when using JWT tokens in AdonisJS, apply defense-in-depth measures that reduce computational cost per request and limit abuse. Start by enforcing strict token validation rules: verify signatures, validate exp (expiration), nbf (not before), and iss (issuer) early, and reject tokens with excessive or unexpected claims before performing expensive operations. Use asynchronous, non-blocking verification and avoid synchronous CPU-heavy operations in your auth middleware. For high-volume services, introduce lightweight rate limiting on authentication-related routes (token issuance, refresh, and revocation) using a sliding window or token-bucket algorithm to cap requests per IP or per user identifier.
Caching valid token verification results for a short window can reduce repeated cryptographic work for identical tokens within a single request lifecycle, but ensure cache invalidation respects revocation signals. Configure short access token lifetimes and use refresh token rotation with strict quotas to prevent token churn abuse. Implement request size limits and schema validation for token payloads to reject malformed or oversized claims before they enter your verification logic. Below are concrete code examples for AdonisJS that demonstrate these practices.
Example: Secure JWT middleware with early validation and rate limiting
// start/kernel.ts
import { middleware } from '@adonisjs/core'
import { verify } from 'jsonwebtoken'
import { RateLimiterMemory } from 'rate-limiter-flexible'
// Simple in-memory rate limiter for auth endpoints (use Redis in production)
const authLimiter = new RateLimiterMemory({
points: 10, // 10 requests
duration: 1, // per second
keyPrefix: 'auth_ip',
})
export const authJwt = middleware(async (ctx, next) => {
const authHeader = ctx.request.header('authorization')
if (!authHeader || !authHeader.startsWith('Bearer ')) {
ctx.response.status(401).json({ error: 'Unauthorized' })
return
}
const token = authHeader.split(' ')[1]
// Basic sanity checks to reduce CPU load
if (token.length > 4096) {
ctx.response.status(400).json({ error: 'Token too large' })
return
}
try {
// Verify token with strict options
const decoded = verify(token, process.env.JWT_SECRET!, {
algorithms: ['HS256'],
clockTolerance: 5,
})
// Attach minimal required claims to context
ctx.auth = {
sub: decoded.sub,
exp: decoded.exp,
}
await next()
} catch (error) {
ctx.response.status(401).json({ error: 'Invalid token' })
}
})
// Example route with rate limiting applied
Route.get('/profile', [authJwt], async ({ auth }) => {
return { user_id: auth.sub }
})
Example: Rate limiting on token issuance and refresh
// start/Controllers/AuthController.ts
import { schema, rules } from '@ioc:Adonisjs/Core/Validator'
import { RateLimiterMemory } from 'rate-limiter-flexible'
const tokenRequestLimiter = new RateLimiterMemory({
points: 5,
duration: 60, // 5 requests per minute per identifier
keyPrefix: 'token_req',
})
export default class AuthController {
public async login({ request, response }) {
const bodySchema = schema.create({
identifier: schema.string({}, [rules.email()]),
password: schema.string({}, [rules.minLength(8)]),
})
const payload = await request.validate({ schema: bodySchema })
// Apply rate limiting by identifier to prevent flooding
try {
await tokenRequestLimiter.consume(payload.identifier)
} catch {
return response.status(429).json({ error: 'Too many requests' })
}
// Perform authentication and issue short-lived JWT
const user = await User.findBy('email', payload.identifier)
if (!user || !user.verifyPassword(payload.password)) {
return response.status(401).json({ error: 'Invalid credentials' })
}
const token = jwt.sign(
{ sub: user.id, scope: 'access' },
process.env.JWT_SECRET!,
{ expiresIn: '15m', algorithm: 'HS256' }
)
return { access_token: token, expires_in: 900 }
}
}Related CWEs: resourceConsumption
| CWE ID | Name | Severity |
|---|---|---|
| CWE-400 | Uncontrolled Resource Consumption | HIGH |
| CWE-770 | Allocation of Resources Without Limits | MEDIUM |
| CWE-799 | Improper Control of Interaction Frequency | MEDIUM |
| CWE-835 | Infinite Loop | HIGH |
| CWE-1050 | Excessive Platform Resource Consumption | MEDIUM |