HIGH log injectionadonisjsjwt tokens

Log Injection in Adonisjs with Jwt Tokens

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

Log injection occurs when untrusted input is written directly into application logs without sanitization or formatting control. In AdonisJS applications that use JWT tokens for authentication, the combination of structured JSON logging and token-derived payloads can amplify the risk if developer assumptions about safety prove incorrect.

AdonisJS typically logs authentication events, token validation results, and request metadata. When a JWT token is processed, claims such as sub (subject/user identifier), roles, or scopes are often extracted and written to logs. If these values contain newline characters, structured delimiters, or control sequences, an attacker can inject additional log entries or manipulate log formatting. Newline characters (\n, \r) are especially dangerous because they enable log forging, where fabricated entries appear to originate from the same context as legitimate authentication events. This can mislead incident responders or obscure real attack activity.

The JWT specification allows claims to contain string values, and while AdonisJS provides JWT utilities for verification and payload decoding, it does not enforce strict schema validation on incoming token contents. An attacker can supply a token with a sub claim formatted as attacker\nusername: forged. When the application logs this value, the newline splits the entry into multiple lines, creating a deceptive log record that may appear to be a separate authentication event. In environments where logs are aggregated and indexed, these forged lines can break parsing rules and distort analytics.

Another vector involves logging the raw token or portions of it. Developers sometimes log the entire JWT for debugging purposes. Because JWTs use dot-separated segments, a malicious payload in the third segment (payload) can include characters that interfere with log parsers, such as JSON control sequences or comment-like syntax. Without output encoding or structured serialization, these characters can cause misalignment in log ingestion pipelines. The risk is compounded when logs are used for security monitoring, as injected content may trigger false positives or mask genuine anomalies.

AdonisJS middleware that logs token metadata without normalization is vulnerable to these issues. For example, a custom authentication hook might extract user.id and token.exp and write them to a log file using a simple string concatenation. If the token claims are not validated for format and encoded before logging, the application effectively provides an attacker a channel to manipulate log integrity. This does not require authentication bypass; it exploits the logging layer directly through crafted JWT input.

Mitigating log injection in this context requires treating JWT-derived data as untrusted input. Even though the token is cryptographically signed, its claims should not be assumed safe for logging. The application must sanitize values before they reach the logging layer, ensuring that newline and delimiter characters are either removed or encoded. Structured logging formats should be enforced so that each log entry is a valid, parseable JSON object, preventing injected newlines from splitting entries. These controls preserve log reliability and support accurate forensic analysis.

Jwt Tokens-Specific Remediation in Adonisjs — concrete code fixes

Remediation focuses on sanitization, structured logging, and strict claim validation before any data is written to logs. Below are concrete, idiomatic AdonisJS examples that demonstrate safe handling of JWT tokens.

First, avoid logging raw token values or concatenated strings that include untrusted claims. Instead, normalize and encode values explicitly. The following example shows a middleware that safely logs authentication events using a structured object, ensuring newline and control characters are handled by the JSON serializer.

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

export default class JwtLoggingMiddleware {
  public async handle({ request, auth }, next) {
    await next()

    // Only log after authentication succeeds
    if (auth.user) {
      const token = request.auth?.token?.toString()
      const payload = request.auth?.token?.payload

      // Sanitize values used in logs
      const safeLog = {
        event: 'auth_token_validated',
        user_id: String(payload?.sub ?? '').replace(/[\r\n]+/g, '_'),
        exp: Number(payload?.exp) || null,
        issuer: String(payload?.iss ?? '').replace(/[\r\n]+/g, '_'),
      }

      // Use structured logging (e.g., pino or console JSON)
      console.log(JSON.stringify(safeLog))
    }
  }
}

This approach ensures that any newline or carriage return in string claims is replaced before serialization, preventing log entry splitting. By converting values explicitly with String() and applying a global regex replacement for \r and \n, the log remains parseable even if the token contains malicious input.

Second, validate JWT claims against a strict schema before using them in logs or business logic. AdonisJS works well with validation libraries, and using a schema enforces acceptable formats for identifiers and timestamps.

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

const tokenClaimSchema = schema.create({
  sub: schema.string({}, [ 'format:uuid' ]), // or another constrained format
  iss: schema.string.optional({}, [ 'alpha' ]),
  exp: schema.number(),
})

export async function validateAndLogToken(tokenString: string) {
  try {
    const payload = jwt.verify(tokenString, publicKey)
    const validated = tokenClaimSchema.validate(payload)

    console.log(JSON.stringify({
      event: 'token_verified',
      user_id: String(validated.sub).replace(/[\r\n]/g, ''),
      exp: validated.exp,
    }))
  } catch (error) {
    console.error(JSON.stringify({
      event: 'token_validation_failed',
      error: String(error.message).replace(/[\r\n]/g, '_'),
    }))
  }
}

Here, the schema restricts claim formats, reducing the likelihood of unexpected characters. The fallback error logging also sanitizes messages to avoid secondary injection through log metadata.

Finally, configure your logging framework (for example, Pino or Console) to output structured JSON by default. This ensures that each log line is a single, coherent JSON object. Even if untrusted data somehow reaches the logger, structured formatting prevents newline-based injection from corrupting multiple entries. Combine this with runtime monitoring for patterns like repeated newlines in user_id fields to detect attempted log injection in production.

ApproachRisk ReductionImplementation Complexity
Structured JSON logging with encoded valuesHigh — prevents newline-based forgeryLow — minimal code changes
Claim schema validation before loggingHigh — enforces expected formatsMedium — requires schema definitions
Avoid logging raw tokensMedium — reduces sensitive data exposureLow — policy change

Frequently Asked Questions

Can log injection via JWT tokens affect security monitoring systems?
Yes. Injected newline characters can split log entries or create fabricated events, which may mislead security monitoring, cause incorrect alerting, and reduce confidence in log-based incident response.
Does AdonisJS provide built-in log sanitization for JWT claims?
No. AdonisJS does not automatically sanitize JWT-derived data before logging. Developers must explicitly validate and encode values to prevent injection when writing to logs.