HIGH xss cross site scriptingadonisjsjwt tokens

Xss Cross Site Scripting in Adonisjs with Jwt Tokens

Xss Cross Site Scripting in Adonisjs with Jwt Tokens — how this specific combination creates or exposes the vulnerability

Cross-site scripting (XSS) in an AdonisJS application that uses JWT tokens typically arises when an application embeds data from an untrusted source into HTML/JavaScript without proper sanitization or escaping, and the data is included in a context where it can be executed. JWT tokens themselves are cryptographically signed and encoded, but they are not immune to XSS when their payload is used in a browser context. If your AdonisJS API issues a JWT and that token’s claims (for example, a user-supplied name or role) are later rendered into a page served to the browser without escaping, an attacker can inject script content that executes in other users’ sessions.

Consider a scenario where an AdonisJS route decodes a JWT on the server, extracts a user-supplied field such as displayName, and passes it to a view template. If the template directly interpolates the value without escaping, an attacker who can influence the JWT payload (for instance, via an insecure sub claim or a custom claim set by a compromised admin) can cause stored or reflected XSS. Another relevant pattern is an admin endpoint that returns user data (including decoded JWT claims) to a frontend framework that then dynamically renders HTML using JavaScript; if the frontend inserts JSON directly into the DOM (e.g., using innerHTML), malicious payloads encoded in the JWT can execute.

Additionally, if your AdonisJS application embeds a JWT into a URL as a query parameter or fragment and that URL is rendered in HTML without proper encoding, an attacker may craft a token containing a script that gets executed when the URL is visited. This often occurs in single-page app flows where the client parses the token from the URL and uses it to personalize the UI. The risk is heightened when developers assume the signature guarantees safety and overlook output encoding in templates and client-side rendering.

In practice, XSS with JWTs in AdonisJS is less about breaking the token validation and more about failing to treat data extracted from the token as untrusted input. Any time a JWT claim is placed into HTML, JavaScript, or CSS without context-aware escaping, you open the door to injection. The token’s integrity does not prevent the browser from executing malicious strings that were intentionally placed there by an attacker who controlled part of the payload or tricked an admin into issuing a token containing harmful data.

To detect this class of issue, scanners check whether responses that include tokens or data derived from tokens reflect user-controlled values into HTML/JS without sanitization. They also look for endpoints that issue tokens with overly permissive claims and UI flows that deserialize tokens in the browser and inject them into the DOM. Remediation is not about changing how the token is signed, but about ensuring that any data from the token is escaped for the target context and that least-privilege claims are used.

Jwt Tokens-Specific Remediation in Adonisjs — concrete code fixes

Remediate XSS when using JWT tokens in AdonisJS by treating all claims as untrusted and applying context-specific escaping before rendering. Below are concrete patterns and code examples to safely handle JWT data in templates and APIs.

1. Server-side rendering with escaped output

When rendering HTML views, escape data extracted from the JWT using the template engine’s escaping utilities. For Edge templates (default in AdonisJS), use {{ }} which auto-escapes by default. Avoid marking content as safe unless you have sanitized it.

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

export default class UserController {
  public async profile({ auth, view }: HttpContextContract) {
    const user = auth.user!
    // JWT is typically verified by an auth provider; user is already available
    // If you explicitly decode a token, use:
    // const token = request.header('authorization')?.replace('Bearer ', '')
    // const payload = Jwt.verify(token, 'YOUR_SECRET')
    return view.render('profile', {
      displayName: user.displayName,
      // Edge will escape this by default
    })
  }
}

In the Edge template (resources/views/profile.edge), ensure you do not use !{ } unless you have sanitized the content:

<div>Welcome, {{ displayName }}</div>

2. API responses: encode for HTML/JS contexts

If your endpoint returns JSON that will be rendered client-side, do not rely on the client to sanitize. Instead, ensure the server does not send executable strings in fields that will be injected as HTML. If you must send rich content, provide it as sanitized HTML or use a strict allowlist.

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

export default class UserController {
  public async me({ auth }: HttpContextContract) {
    const user = auth.user!
    // Return plain data; let the client handle rendering safely
    return {
      id: user.id,
      // Keep free-text fields as plain strings; escape on the client if inserting into HTML
      displayName: user.displayName,
      roles: user.roles.map((r) => r.name)
    }
  }
}

3. Avoid injecting JWT claims into href/src/style attributes

Do not directly embed JWT claims into URLs or style attributes without encoding. Use libraries to build URLs safely and avoid javascript: handlers.

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

export default class LinkController {
  public build({ auth }: HttpContextContract) {
    const user = auth.user!
    // Safe: use a path builder, not string concatenation with untrusted input
    const url = `/dashboard?user=${encodeURIComponent(user.id)}`
    return { url }
  }
}

4. Validate and limit JWT claims

Issue tokens with minimal claims and validate them server-side. In AdonisJS, configure your auth provider to expect strict payload shapes and reject tokens with unexpected fields.

// Example JWT verification with strict payload checks
import { jwtVerify } from 'jose'

const encoder = new TextEncoder()
const token = 'YOUR_JWT_HERE'
const { payload } = await jwtVerify(token, encoder.encode(process.env.JWT_SECRET!))
if (typeof payload.name !== 'string' || payload.name.length > 128) {
  throw new Error('Invalid token claims')
}

5. Client-side handling

If you must parse a JWT in the browser, avoid inserting raw claims into innerHTML. Use textContent or a safe templating library that escapes by default.

// Instead of: element.innerHTML = tokenPayload.name
element.textContent = tokenPayload.name

6. Security headers and CSP

Add Content-Security-Policy headers to mitigate the impact of any accidental injection. In AdonisJS, use middleware to set headers.

export default class SecurityHeadersMiddleware {
  public async handle(ctx, next) {
    ctx.response.headers.set(
      'Content-Security-Policy',
      "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline';"
    )
    await next()
  }
}

Related CWEs: inputValidation

CWE IDNameSeverity
CWE-20Improper Input Validation HIGH
CWE-22Path Traversal HIGH
CWE-74Injection CRITICAL
CWE-77Command Injection CRITICAL
CWE-78OS Command Injection CRITICAL
CWE-79Cross-site Scripting (XSS) HIGH
CWE-89SQL Injection CRITICAL
CWE-90LDAP Injection HIGH
CWE-91XML Injection HIGH
CWE-94Code Injection CRITICAL

Frequently Asked Questions

Does signing a JWT prevent XSS in AdonisJS?
No. A JWT signature ensures integrity and authenticity of the payload, but it does not prevent the browser from executing malicious scripts if untrusted data from the token is rendered into HTML/JS without proper escaping. Always treat JWT claims as untrusted input and escape for the target context.
Should I store sensitive data in JWT claims to avoid XSS?
You should avoid storing sensitive data in JWT claims, as tokens are often transmitted and may be exposed in logs or browsers. Minimize claims to non-sensitive identifiers and use server-side sessions or encrypted storage for secrets. XSS mitigation relies on output encoding and strict Content Security Policies, not on hiding data in tokens.