HIGH injection flawsadonisjsjwt tokens

Injection Flaws in Adonisjs with Jwt Tokens

Injection Flaws in Adonisjs with Jwt Tokens — how this specific combination creates or exposes the vulnerability

Injection flaws in AdonisJS when JWT tokens are involved typically arise when untrusted data from a token (claims, payload, or headers) is used to construct queries, file paths, commands, or dynamic object access without validation or escaping. Because JWTs are often treated as trustworthy once verified, developers may directly interpolate token contents into database queries, route parameters, or filesystem operations, reintroducing injection risks. For example, using a user ID from a JWT payload directly in an AdonisJS Lucid query without sanitization can enable SQL injection if the token source is compromised or the verification step is misconfigured.

Consider an endpoint that retrieves a user profile using an ID from a JWT: if the ID is not strictly validated as an integer before use, an attacker who can influence the token (via weak signing keys, algorithm confusion, or stolen tokens) can manipulate the query. Similarly, injecting token claims into dynamic finders or dynamic model properties can lead to NoSQL-like injection patterns in JavaScript/TypeScript ORMs. Another scenario involves using JWT header parameters (e.g., kid) to locate keys or switch databases without strict allowlisting, which can enable server-side request forgery (SSRF) or path traversal when combined with filesystem utilities.

AdonisJS provides middleware and guards to verify JWTs, but the framework does not automatically sanitize data extracted from tokens. If developers bypass or incorrectly apply guards, or if they assume token integrity equals data safety, injection surfaces expand. For instance, using raw token payload values in where clauses, orderBy, or dynamic schema fields without normalization can expose injection paths. Even logging or error handling that echoes token claims can lead to injection-assisted log forging or verbose errors that reveal internal structures.

Real-world patterns that increase risk include:

  • Directly passing ctx.request.header().authorization extract logic into dynamic query builders without type checks.
  • Using JWT sub or custom claims to build file paths for uploads or reads without path normalization.
  • Allowing token-based role claims to dynamically switch database connections or ORM models without strict allowlists.

These patterns show how the combination of AdonisJS flexibility and JWT convenience can unintentionally reintroduce injection if token data is treated as safe input. The framework does not implicitly trust JWT payloads, but developers must enforce strict validation, type coercion, and allowlisting when using token-derived data in any operation that interacts with databases, files, or external systems.

Jwt Tokens-Specific Remediation in Adonisjs — concrete code fixes

Remediation focuses on strict validation, type coercion, and avoiding direct use of token claims in sensitive operations. Always treat JWT payloads as untrusted input, even after verification. Below are concrete, idiomatic AdonisJS examples that demonstrate secure patterns.

1. Validate and coerce JWT claims before use

Use Joi or schema validation on decoded claims. Do not rely on runtime truthiness or implicit casting.

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

export default class UserController {
  public async show({ auth, request }: HttpContextContract) {
    const tokenUser = auth.user // assume JWT guard populated this
    // Validate claim types explicitly
    const payloadSchema = schema.create({
      sub: schema.string({ trim: true, normalize: true }),
      role: schema.enum(['admin', 'user', 'guest']),
      exp: schema.number()
    })

    const validated = payloadSchema.validate({
      sub: tokenUser.id,
      role: tokenUser.role,
      exp: Math.floor(Date.now() / 1000)
    })

    // Use validated data, not raw token fields
    const safeUserId = validated.sub
    const safeRole = validated.role
    // Proceed with safe, typed values
  }
}

2. Parameterize database queries; never interpolate token values

Use Lucid query builder with bound parameters or static where clauses. Do not build dynamic where objects from token claims without allowlisting.

import User from 'App/Models/User'

export default class ProfileController {
  public async index({ auth }: HttpContextContract) {
    const user = auth.user
    // Safe: using bound parameters via where
    const profile = await User.query()
      .where('id', user.id) // id validated as integer or string UUID
      .preload('roles')
      .firstOrFail()

    // Unsafe pattern to avoid:
    // await User.query().where(userSuppliedKey, userSuppliedValue)
  }
}

3. Restrict dynamic model or field references

If you must use token-derived keys, strictly allowlist them and avoid direct dynamic access.

const ALLOWED_SORT_FIELDS = ['id', 'email', 'created_at']
const sortBy = request.input('sortBy', 'id')

if (!ALLOWED_SORT_FIELDS.includes(sortBy)) {
  throw new Error('Invalid sort field')
}

const users = await User.query().orderBy(sortBy, 'asc')

// Avoid: const users = await User.query().orderBy(request.input('sortBy'), 'asc')

4. Secure file operations when using token claims in paths

Normalize and restrict paths; do not concatenate raw token strings to filesystem paths.

import Drive from '@ioc:Adonis/Core/Drive'
import { extname, basename } from 'path'

export default class FileController {
  public async read({ auth, params }: HttpContextContract) {
    const user = auth.user
    const requestedFile = params.file
    const ext = extname(requestedFile)
    const safeName = basename(requestedFile, ext)

    // Ensure file belongs to user and path is controlled
    const resolvedPath = `uploads/${user.id}/${safeName}.${ext}`
    if (!resolvedPath.startsWith('uploads/')) {
      throw new Error('Invalid path')
    }
    return Drive.get(resolvedPath)
  }
}

5. Guard against algorithm confusion and key misuse

Configure JWT verification to reject unexpected algorithms and enforce strong keys.

// In start/auth.ts
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import { middleware } from '@ioc:Adonis/Addons/Auth'

export const guard = {
  name: 'jwt',
  middleware: middleware('jwt', {
    secret: process.env.JWT_SECRET,
    algorithm: 'HS256', // enforce a single algorithm
    allowedInsecureAlgorithms: false
  }) as any
}

// In a route handler, after guard applies:
const user = auth.user // properly verified with enforced algorithm and key

6. Avoid logging or exposing token claims in errors

Ensure error messages do not echo raw token payloads, which can aid injection or information disclosure.

try {
  // some operation using token-derived data
} catch (error) {
  // Do not log raw token claims
  logger.error('Profile fetch failed', { userId: auth.user.id })
  throw error
}

These patterns reduce injection risk by ensuring JWT-derived data is validated, coerced, and never directly interpolated into sensitive operations. Combine these practices with route-level guards and strict allowlists to maintain a secure AdonisJS + JWT implementation.

Frequently Asked Questions

Can an attacker exploit JWT header parameters like kid in Adonisjs to trigger injection?
Yes, if the application uses the kid header to dynamically select keys, secrets, or database connections without strict allowlisting, it can lead to injection or SSRF. Always validate and restrict such parameters.
Is simply verifying a JWT in Adonisjs sufficient to prevent injection?
No. Verification confirms token validity and origin, but does not sanitize or validate individual claims. Always validate and coerce claims before using them in queries, file paths, or dynamic object access.