HIGH log injectionadonisjsmutual tls

Log Injection in Adonisjs with Mutual Tls

Log Injection in Adonisjs with Mutual Tls — how this specific combination creates or exposes the vulnerability

Log injection occurs when untrusted input is written directly into application or system logs without proper sanitization. In AdonisJS, this typically arises through logging utilities such as the built-in Logger or third-party packages (e.g., pino or winston integrations) where request-derived data—like user identifiers, IP addresses, or headers—is concatenated into log messages. If newline characters or structured delimiters are not escaped or isolated, an attacker can inject additional log entries, obscure true event sequences, or forge audit trails.

When mutual TLS (mTLS) is enabled, the client certificate presented during the TLS handshake becomes a significant new input vector. AdonisJS applications that inspect the client certificate—commonly to perform authorization, tenant identification, or request attribution—may inadvertently incorporate certificate fields (subject DN, issuer, serial, or SANs) into logs. Because these values are controlled by the client (within CA policy), they are untrusted from the application’s perspective. If certificate metadata is logged verbatim, an attacker can craft certificate attributes containing newline or control characters, leading to log injection. Moreover, mTLS setups often terminate TLS at a proxy or load balancer; if AdonisJS logs the perceived client IP from headers like x-forwarded-for alongside certificate details, an attacker who can influence those headers (or the proxy configuration) can compound the injection. The combination of mTLS-derived data and insufficient input validation in logging code expands the attack surface, enabling event correlation manipulation, social engineering via forged logs, or bypass of detection rules that rely on log integrity.

Consider an AdonisJS route that logs the client certificate subject for audit:

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

export default class SessionsController {
  public authenticate({ request, response }: HttpContextContract) {
    const cert = request.socket.getPeerCertificate?.()
    if (cert && cert.subject) {
      // Risk: cert.subject may contain newlines or delimiters
      logger.info('Client authenticated', { subject: cert.subject })
    }
    // ... login logic
  }
}

If an attacker presents a certificate with subject=CN=Evil\nC=Admin, the log line can be split into two entries, breaking chronological analysis. Additionally, if the application logs request headers influenced by mTLS contexts (e.g., mapping certificate serial to a user ID), header values that include line breaks or JSON-like fragments can further distort log structure. The risk is not theoretical: similar log injection patterns have been observed in frameworks where structured metadata mixes with free-form text, and they map to OWASP API Top 10 controls around audit integrity.

Mutual Tls-Specific Remediation in Adonisjs — concrete code fixes

Remediation focuses on strict input validation, structured logging, and separation of concerns. Never trust certificate fields or headers that originate from the client. Implement canonicalization (trim, reject control characters) before emitting any log entry.

1) Validate and sanitize certificate data before logging

Extract only the necessary fields and sanitize them. For example, allow only alphanumeric characters in identifiers and replace newlines or control characters.

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

function sanitizeCertSubject(subject: string): string {
  // Remove newlines, carriage returns, and other control chars; limit length
  return subject.replace(/[\r\n\x00-\x1F\x7F]/g, ' ').trim().slice(0, 256)
}

export default class SessionsController {
  public authenticate({ request, response }: HttpContextContract) {
    const cert = request.socket.getPeerCertificate?.()
    if (cert && cert.subject) {
      const safeSubject = sanitizeCertSubject(cert.subject)
      logger.info('Client authenticated', { subject: safeSubject })
    }
    // ... login logic
  }
}

2) Use structured logging with explicit fields

Log known, bounded fields rather than raw objects. This makes it easier to detect anomalies and prevents injected lines from blending into structured output.

import logger from '@ioc:Adonis/Core/Logger'
import { DateTime } from 'luxon'

export default class AuditLogger {
  static clientAuth(safeSubject: string, requestId: string) {
    logger.info('auth.client', {
      event: 'client_authenticated',
      timestamp: DateTime.now().toISO(),
      subject: safeSubject,
      requestId,
    })
  }
}

3) Isolate mTLS termination details from application logs

If you must reference certificate metadata, map it to internal, validated identifiers before logging. Avoid logging raw headers influenced by mTLS or proxied environments.

import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import logger from '@ioc:Adonis/Core/Logger'
import { v4 as uuidv4 } from 'uuid'

export default class MtlsAwareController {
  public handle({ request }: HttpContextContract) {
    const cert = request.socket.getPeerCertificate?.()
    const internalId = cert?.serial ? validateSerial(cert.serial) : null
    // Use internalId for logging; never raw cert fields
    logger.debug('mtls.context', { requestId: uuidv4(), internalId })
  }
}

function validateSerial(serial: string): string {
  // Accept only hex digits, limit length
  const clean = serial.replace(/[^0-9A-Fa-f]/g, '').slice(0, 64)
  return clean || 'unknown'
}

4) Enforce header discipline when proxies are involved

If behind a proxy, configure AdonisJS to trust a specific set of headers and validate them. Do not directly use x-forwarded-for for security decisions without strict validation.

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

// In start/hooks.ts or a middleware
export const trustedIps = (ctx: HttpContextContract) => {
  const forwarded = ctx.request.header('x-forwarded-for')
  if (forwarded) {
    // Assume single IP when behind known proxy; reject lists or malformed values
    const ip = forwarded.split(',').pop()?.trim()
    if (ip && /^(?:\d{1,3}\.){3}\d{1,3}$/.test(ip)) {
      ctx.request['clientIp'] = ip
    }
  }
}

Frequently Asked Questions

Can log injection in AdonisJS with mTLS affect compliance reporting?
Yes. Because logs are often used for audit and compliance (e.g., PCI-DSS, SOC2), injected entries can corrupt event timelines and undermine integrity checks. Sanitize and structure logs to preserve auditability.
Does middleBrick assess log injection risks in API scans?
middleBrick performs 12 security checks including Input Validation and Data Exposure. Findings include actionable remediation guidance to help you address issues like improper log handling.