HIGH prototype pollutionadonisjsjwt tokens

Prototype Pollution in Adonisjs with Jwt Tokens

Prototype Pollution in Adonisjs with Jwt Tokens — how this specific combination creates or exposes the vulnerability

Prototype pollution in AdonisJS can intersect with JWT token handling when user-controlled data from token payloads is merged into objects that later influence behavior or serialization. A JWT issued by an identity provider or derived from application logic may include dynamic claims that an endpoint merges into configuration objects, request context, or response helpers. If the application uses shallow merges (e.g., Object.assign or lodash merge) on token-derived objects without validating or copying properties, an attacker can inject keys such as __proto__, constructor, or prototype fields that mutate shared prototypes across requests.

In AdonisJS, this commonly occurs when route middleware or auth guards consume decoded JWT payloads and pass them to services that later construct responses or query databases. For example, a payload containing { "role": "user", "__proto__": { "isAdmin": true } } can, when merged into an options object, affect subsequent object instantiation or permission checks. Because AdonisJS often serializes objects to JSON for logging or error reporting, a polluted prototype can lead to unintended data exposure, such as leaking sensitive fields attached to Object.prototype or altering toJSON behavior. The risk is amplified when tokens carry user-supplied claims (e.g., from OAuth or custom extensions) that the application naively trusts and merges without deep cloning or schema validation.

An attacker may also exploit prototype pollution to tamper with rate limiter state, configuration caches, or security checks that rely on shared objects across requests. If the JWT is used to derive identifiers passed to database queries or authorization helpers, a modified prototype key can change equality checks or serialization output, indirectly enabling privilege escalation or data exposure. Because the JWT signature may still validate (if the server does not enforce strict claim validation), the malicious payload appears legitimate, bypassing superficial integrity checks. The effective attack surface therefore includes any code path that deserializes a token, merges its claims into mutable structures, and later uses those structures in security-sensitive decisions or object construction within the AdonisJS runtime.

Jwt Tokens-Specific Remediation in Adonisjs — concrete code fixes

Remediation focuses on strict validation, deep cloning, and avoiding unsafe merges of JWT payload data. Always verify issuer, audience, and required claims before using token contents. Use a validation layer that whitelists expected fields and rejects unknown keys, especially __proto__, constructor, and prototype. Prefer immutable operations and explicit property extraction instead of merging entire payloads.

Example 1: Safe JWT verification and claim extraction in a route middleware

import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import { schema, rules } from '@ioc:Adonis/Core/Validator'
import { authenticator } from 'otplib'

const jwtClaimSchema = schema.create({
  token: schema.string.optional(),
  payload: schema.object().shape({
    sub: schema.string.optional(),
    email: schema.string.optional(),
    role: schema.enum(['user', 'admin']).optional(),
    // explicitly allow only expected claims
  }).optional(),
})

export default class AuthMiddleware {
  public async handle({ request, response, auth }: HttpContextContract, next: () => Promise) {
    const token = request.header('authorization')?.replace('Bearer ', '')
    if (!token) {
      return response.unauthorized('Missing token')
    }

    // Verify and decode safely; do not merge entire payload
    const { payload } = await auth.use('jwt').verifyAndDecode(token)

    // Validate against a strict schema to discard unexpected claims
    const validated = await validator.validate({
      schema: jwtClaimSchema,
      data: { token, payload },
    })

    // Explicitly extract needed fields; avoid Object.assign on payload
    const { sub, email, role } = validated.payload || {}
    request.authUser = { id: sub, email, role }
    await next()
  },
}

Example 2: Avoiding prototype pollution when constructing response or context objects

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

export default class UserController {
  public async profile({ request, auth }: HttpContextContract) {
    const tokenUser = auth.user
    // Do NOT merge request body or payload directly into prototypes
    const safeData = {
      id: tokenUser.id,
      email: tokenUser.email,
      role: tokenUser.role,
    }

    // Explicitly build response without mutating shared objects
    return {
      user: safeData,
      meta: { generatedAt: new Date().toISOString() },
    }
  },
}

Example 3: Using deep clone when you must merge token-derived data

import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import { cloneDeep } from 'lodash'

export default class SettingsController {
  public async update({ request, auth }: HttpContextContract) {
    const body = request.only(['theme', 'notifications'])
    const tokenUser = auth.user
    // Clone before merge to prevent prototype modification
    const merged = cloneDeep({
      id: tokenUser.id,
      settings: {},
    })
    merged.settings = { ...body }
    // Use merged safely
    await tokenUser.merge({ settings: merged.settings }).save()
  },
}

Frequently Asked Questions

Can an attacker exploit prototype pollution if the JWT is signed but the server trusts all claims?
Yes. Even with a valid signature, if the server merges the entire decoded payload into mutable objects without schema validation or deep cloning, attacker-controlled keys like __proto__ can alter object prototypes and affect runtime behavior.
Does using the middleBrick CLI or Dashboard prevent prototype pollution in AdonisJS JWT flows?