HIGH rate limiting bypassadonisjsjwt tokens

Rate Limiting Bypass in Adonisjs with Jwt Tokens

Rate Limiting Bypass in Adonisjs with Jwt Tokens — how this specific combination creates or exposes the vulnerability

AdonisJS is a Node.js web framework that commonly uses JWT tokens for stateless authentication. When JWT-based auth is combined with rate limiting configured only at the route or middleware layer, attackers can bypass intended request caps by exploiting how identity and scope are tied to the token rather than to the end user or client. In a typical setup, a developer applies a global or route-specific rate limiter (for example, using an AdonisJS provider or a reverse proxy rule) that limits by IP address or by a fixed number of requests per window. JWTs, however, embed identity in the token payload; once a token is issued, any request presenting that token is treated as an authenticated user, and the limiter may treat the token as a single, reusable credential.

This creates a bypass when the rate limiter does not also validate or factor the token’s internal claims (such as user ID, roles, or scopes) and does not enforce per-identity or per-scope limits. An attacker who obtains a valid JWT—through insecure storage, token leakage, or a compromised client—can generate many requests that all carry the same token. Because the limiter sees a steady stream of requests from the same token (and often from the same IP if the attacker is careful), it may not throttle or block them, effectively circumventing the intended protection. Additionally, if the application issues long-lived tokens or does not rotate secrets, a single leaked token can be reused across many requests, magnifying the exposure.

The risk is further influenced by how endpoints are designed. If an endpoint relies on JWT claims to determine authorization but does not enforce rate limits based on those claims, two different users with different permissions could be subjected to different effective limits even when their usage patterns should be treated equally. Insecure configurations might also allow token substitution or replay, where an attacker reuses a token from a low-privilege context to abuse higher-rate endpoints. Because the limiter and the auth layer are not tightly coupled in terms of identity-aware throttling, the attack surface includes token acquisition vectors and the application’s token issuance logic.

middleBrick’s LLM/AI Security checks highlight this class of issue by testing for system prompt leakage and injection attempts that could expose token handling logic, alongside active prompt injection probes that simulate attempts to override instructions or exploit cost mechanisms. These tests can surface how an API’s authentication and rate limiting interplay when LLM-related endpoints are involved. Observational scans also flag endpoints with missing or inconsistent rate limiting across authenticated routes, helping teams detect where identity-aware throttling is absent.

Real-world patterns mirror this scenario: an API issues a JWT with a broad scope and long expiration; a route uses that token for authorization but applies a simple IP-based rate limit; an attacker captures the token and fires requests at a high rate. Because the limiter does not tie usage to the token’s subject or to per-identity quotas, the attack bypasses protections that were intended to limit abuse. This is not a flaw in JWT itself but in how the framework and middleware integrate token-based identity with request governance.

To detect such issues, scans should compare the OpenAPI/Swagger spec definitions (including full $ref resolution) with runtime behavior. If the spec declares security schemes based on JWT but rate limiting is defined only at the IP or global level, the scan can highlight the mismatch. Runtime tests can then validate whether requests with different tokens but the same IP are throttled consistently and whether per-identity limits are enforced.

Jwt Tokens-Specific Remediation in Adonisjs — concrete code fixes

Remediation focuses on coupling identity from the JWT with rate limiting logic, ensuring that limits are applied per principal (for example, user ID or client ID) rather than only by IP or globally. Below are concrete, realistic code examples for AdonisJS that demonstrate how to implement identity-aware rate limiting alongside JWT authentication.

First, ensure your JWT payload includes a stable identifier, such as user ID, and that tokens are issued with appropriate expiration and scope. Then, in your authentication provider or middleware, extract the identity from the token and use it as part of the rate-limiting key.

// Example: JWT payload with user identifier
const jwt = require('@adonisjs/auth')
const { createHash } = require('crypto')

function generateToken(user) {
  return jwt.sign({
    sub: user.id,
    scope: user.role,
    iat: Math.floor(Date.now() / 1000),
    exp: Math.floor(Date.now() / 1000) + (7 * 24 * 60 * 60) // 7 days
  }, process.env.JWT_SECRET)
}

// Middleware to extract identity from token
async function authenticate({ request, auth }, next) {
  const token = request.header('authorization')?.replace('Bearer ', '')
  if (!token) {
    throw new Error('Unauthorized')
  }
  const payload = jwt.verify(token, process.env.JWT_SECRET)
  request.user = { id: payload.sub, role: payload.scope }
  await next()
}

With identity available on request.user, you can implement rate limiting that uses a composite key, such as rate_limit:{userId}:{scope} or a hash derived from token claims. Below is an example using an in-memory store for illustration; in production, use Redis or a distributed store to coordinate limits across instances.

// Identity-aware rate limiter for AdonisJS
const RateLimit = require('rate-limiter-flexible')

const keyExtractor = (req) => {
  if (req.user && req.user.id) {
    return `user:${req.user.id}:${req.user.role}`
  }
  // Fallback for unauthenticated endpoints
  return `ip:${req.ip()}
}

const rateLimiter = new RateLimit({
  points: 100, // 100 requests
  duration: 60, // per 60 seconds
  keyPrefix: 'rl',
  inmemory: { // replace with Redis store in production
    async consume(key, limit) {
      // simplistic in-memory tracking for example
      if (!this.store[key]) this.store[key] = { used: 0, last: Date.now() }
      const entry = this.store[key]
      const now = Date.now()
      if (now - entry.last > 1000 * duration) {
        entry.used = 0
        entry.last = now
      }
      if (entry.used >= limit) return { done: true, msBeforeNext: 1000 * 60 }
      entry.used += 1
      return { done: false, msBeforeNext: 0 }
    }
  }
})

async function rateLimitMiddleware(ctx, next) {
  const key = keyExtractor(ctx.request)
  try {
    await rateLimiter.consume(key)
    await next()
  } catch (error) {
    ctx.response.status = 429
    ctx.response.body = { error: 'Too Many Requests' }
  }
}

Apply this middleware after authentication so that request.user is populated. This ensures the limiter uses the JWT-derived identity rather than only IP, making it harder to bypass by rotating IPs while keeping a valid token. For granular controls, define different point/duration pairs per scope (for example, stricter limits for admin tokens).

Additionally, rotate JWT secrets periodically and keep token lifetimes short to reduce the window for reuse if a token is leaked. Combine these measures with logging and anomaly detection to spot bursts of requests that share the same token identity. middleBrick’s Pro plan supports continuous monitoring and can integrate with CI/CD pipelines to flag configurations where rate limiting does not incorporate identity from JWTs, helping you catch mismatches before deployment.

Finally, validate that your OpenAPI/Swagger spec reflects the combined security and rate-limiting expectations, with full $ref resolution so definitions are consistent with runtime behavior. The CLI tool can be used in scripts to assert that identity-aware rules are present, and the dashboard can track scores over time to ensure improvements persist.

Related CWEs: resourceConsumption

CWE IDNameSeverity
CWE-400Uncontrolled Resource Consumption HIGH
CWE-770Allocation of Resources Without Limits MEDIUM
CWE-799Improper Control of Interaction Frequency MEDIUM
CWE-835Infinite Loop HIGH
CWE-1050Excessive Platform Resource Consumption MEDIUM

Frequently Asked Questions

Why can a valid JWT token bypass IP-based rate limits in AdonisJS?
Because the rate limiter may key only on IP or a global counter, while the framework treats any request presenting a valid token as an authenticated identity. Without coupling limits to claims like user ID or scope, an attacker reusing a token can exceed intended request caps.
How can I verify that my rate limiting is identity-aware for JWT-authenticated endpoints?
Compare your OpenAPI/Swagger spec (with full $ref resolution) to runtime behavior: ensure rate-limiting definitions reference identity (e.g., user ID or scope) rather than only IP, and test with different tokens from the same IP to confirm distinct limits.