HIGH double freeadonisjsjwt tokens

Double Free in Adonisjs with Jwt Tokens

Double Free in Adonisjs with Jwt Tokens — how this specific combination creates or exposes the vulnerability

Double free is a memory safety class of vulnerability that can manifest in native addons or in garbage-collected runtimes that interact with native bindings. In the context of AdonisJS, which is a Node.js framework, a double-free pattern typically arises when lifecycle hooks or token utilities free or release an underlying native resource (such as a buffer or a cryptographic key handle) more than once. When JWT tokens are involved, this can occur through misuse of signing/verification libraries, improper handling of key material, or repeated calls that trigger cleanup routines on shared native objects.

One realistic scenario involves the way AdonisJS applications manage JSON Web Key Sets (JWKS) or symmetric keys for signing and verifying tokens. If a developer caches a signing key object and inadvertently calls a release or finalize routine—perhaps via a custom provider or through bindings in a native module—both the framework internals and the application code may attempt to free the same memory. This can be triggered during token refresh flows where a middleware re-initializes a key provider on each request without guarding against duplicate disposal, or when hot-reloading modules in development causes constructors to run twice on the same native handle.

Another vector arises when JWT tokens carry sensitive claims and the application performs multiple passes over the same cryptographic context. For example, if an app validates a token, extracts a payload, and then reuses the same key object in a secondary signing operation within the same event loop tick, improper reference counting in native bindings can lead to use-after-free or corruption. In AdonisJS, this can surface when packages that wrap node-jose or jsonwebtoken are used incorrectly, such as calling jwt.sign and jwt.verify with overlapping key material that is managed by a shared native handle without proper isolation.

An observable risk pattern is when developers implement custom token utilities that reuse objects across requests. Consider a service that loads a private key once and then repeatedly signs tokens in a loop or across multiple middleware chains without creating fresh contexts. If the native layer is invoked multiple times on the same underlying key handle, the runtime may attempt to free the same memory region twice, leading to instability or potential code execution paths that an attacker can leverage via crafted tokens that force repeated verification or signing attempts.

Because middleBrick scans the unauthenticated attack surface and tests API endpoints for input validation and authentication issues, it can surface indicators that a token endpoint is susceptible to malformed inputs that trigger repeated internal processing. While middleBrick does not inspect native memory management directly, its checks on authentication, property authorization, and input validation can help identify endpoints where malformed JWT tokens cause abnormal behavior that may correlate with double-free conditions in native extensions.

Jwt Tokens-Specific Remediation in Adonisjs — concrete code fixes

To mitigate double-free and related memory-safety issues when using JWT tokens in AdonisJS, focus on deterministic resource handling, isolation of cryptographic contexts, and strict validation of token inputs. Below are concrete, idiomatic code examples that demonstrate safe patterns.

1. Key management: avoid shared mutable key objects

Ensure each signing or verification operation uses a discrete key context. Do not reuse a single key object across multiple requests or token operations.

import { createHmac } from 'crypto'
import { createRemoteJWKSet, jwtVerify } from 'jose'

// Safe: fetch JWKS per verification batch and do not cache raw key handles
const JWKS_URI = 'https://auth.example.com/.well-known/jwks.json'
const jwks = createRemoteJWKSet(new URL(JWKS_URI))

export async function verifyToken(token: string) {
  const { payload } = await jwtVerify(token, jwks, {
    issuer: 'https://auth.example.com/',
    audience: 'api.example.com',
  })
  return payload
}

2. Use framework config for JWT, but isolate signing contexts

AdonisJS allows configuring JWT providers in `start/app.ts`. Keep configuration declarative and avoid manual key reuse in application code.

// start/app.ts
import { defineConfig } from '@ioc:Adonis/Addons/Jwt'

export default defineConfig({
  secret: process.env.JWT_SECRET,
  token: {
    expiresIn: '2h',
  },
  // Ensure provider is initialized once and framework handles lifecycle
})

// In a controller, use the provider without re-instantiating
import { use } from '@ioc:Adonis/Addons/Jwt'

export default class AuthController {
  public async login({ auth, request }) {
    const user = await auth.authenticate()
    const token = use.sign({ sub: user.id, role: user.role })
    return { token }
  }

  public async verify() {
    const payload = use.verify()
    return payload
  }
}

3. Guard against repeated verification and custom middleware pitfalls

Do not call verify multiple times on the same request chain. Centralize validation and ensure token parsing is idempotent but not redundant.

import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import { jwtMiddleware } from '@ioc:Adonis/Addons/Jwt'

// Apply once per route or route group
// Avoid re-verifying inside handlers
export default class AuthMiddleware {
  public async handle(ctx: HttpContextContract, next: () => Promise) {
    const payload = jwtMiddleware.verify(ctx)
    ctx.auth.user = payload
    await next()
  }
}

4. Input validation and schema enforcement

Validate token payloads strictly to prevent malformed inputs from triggering excessive internal processing that may stress native bindings.

import { schema } from '@ioc:Adonis/Core/Validator'

const tokenPayloadSchema = schema.create({
  sub: schema.string.optional(),
  role: schema.union([schema.literal('admin'), schema.literal('user')]),
  iat: schema.number.optional(),
  exp: schema.number.optional(),
})

export function validatePayload(payload: any) {
  const validated = tokenPayloadSchema.validate(payload)
  return validated
}

5. Lifecycle and cleanup discipline in native addons

If your project depends on native modules that manage cryptographic keys, ensure that initialization and disposal are paired and guarded by reference counts. In application code, this means avoiding hot-reload loops and ensuring singleton providers are not recreated per request.

6. Use middleBrick to validate endpoint behavior

Leverage middleBrick’s authentication and input validation checks to detect endpoints that process JWT tokens in ways that could stress underlying native resources. Its OpenAPI/Swagger analysis can also reveal spec-runtime mismatches that lead to repeated token handling.

Frequently Asked Questions

How does reusing JWT key material in AdonisJS lead to double-free risks?
Reusing the same key object across multiple signing or verification operations can cause native bindings to free the same underlying memory more than once, especially when the framework or custom providers invoke dispose or finalize routines on shared handles without reference counting guards.
What specific remediation patterns does middleBrick recommend for JWT handling in AdonisJS?