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().authorizationextract logic into dynamic query builders without type checks. - Using JWT
subor 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.