HIGH token leakageadonisjsmutual tls

Token Leakage in Adonisjs with Mutual Tls

Token Leakage in Adonisjs with Mutual Tls

Token leakage in an AdonisJS application protected with mutual TLS (mTLS) occurs when authentication material—typically a JWT access token or session identifier—is unintentionally exposed beyond the mTLS-secured channel. Even though mTLS binds the client to a trusted certificate, tokens can still leak through application-level mistakes, creating a gap between transport-layer assurance and runtime authorization. The risk is especially pronounced when developers assume mTLS alone is sufficient for authorization and inadvertently expose tokens in logs, error messages, or cross-origin contexts.

In AdonisJS, token leakage often intersects with mTLS in three dimensions:

  • Transport and endpoint exposure: If routes that accept client certificates also expose endpoints that return tokens in URLs, logs, or debug payloads, an attacker who can observe or infer requests (for example, via referrer headers or logs) may capture the token even though the transport was encrypted and authenticated. This can happen when debug routes or verbose error handlers echo the authorization header or token payload back to the client.
  • Application identity confusion: mTLS binds a certificate to a principal (e.g., a client ID). If the application then loads user or API identity from a token rather than from the mTLS-bound principal, the two identity mechanisms can fall out of sync. A token intended for a specific resource scope may be reused across contexts, increasing the blast radius if it is leaked through logs or client-side storage.
  • SSRF and outbound leakage: When an AdonisJS service uses mTLS to authenticate to downstream APIs, a server-side request forgery (SSRF) vulnerability can allow an attacker to force the server to make outbound requests. Because the server presents its mTLS client certificate to the downstream service, the downstream logs may record the token if it is forwarded in headers. This means that SSRF effectively becomes a leakage channel for tokens that the server holds, despite the protection mTLS provides on inbound connections.

To detect these patterns, middleBrick scans the unauthenticated attack surface of an AdonisJS endpoint, including OpenAPI/Swagger 2.0/3.0/3.1 specs with full $ref resolution, and can surface findings such as tokens echoed in error responses or overly permissive CORS that may aid token exfiltration. In LLM-enabled endpoints, it also checks for system prompt leakage and output exposure of credentials, which is especially relevant when LLM features are integrated into the API surface.

Remediation in AdonisJS must address both the application logic and the configuration of mTLS. Ensure that tokens are never reflected in responses, logs, or error details, and enforce strict identity binding by validating the mTLS certificate principal against the token claims rather than relying on the token alone for authorization. Middle-tier checks via middleBrick can validate that endpoints do not inadvertently expose sensitive material and that CORS and error handling follow least-privilege principles.

Mutual Tls-Specific Remediation in Adonisjs

Secure remediation focuses on strict separation between mTLS identity and token usage, careful header handling, and robust error management. Below are concrete patterns and code examples for AdonisJS that reduce the risk of token leakage while preserving mTLS assurance.

1. Configure mTLS in AdonisJS via the HTTP server

AdonisJS relies on the underlying Node.js HTTPS server. You can request client certificates and validate them in middleware. The following example shows server setup and a certificate validation middleware:

// start/server.ts
import { HttpServer } from '@adonisjs/core/types'
import fs from 'fs'

export default class AppServerProvider {
  public async register() {}

  public async boot() {
    const httpsOptions = {
      key: fs.readFileSync('path/to/server.key'),
      cert: fs.readFileSync('path/to/server.crt'),
      ca: fs.readFileSync('path/to/ca.crt'),
      requestCert: true,
      rejectUnauthorized: true,
    }

    const server: HttpServer = use('Adonisjs/Core/Server')
    server.httpServer().listen(3333, '0.0.0.0', () => {
      console.log('Server listening on https://0.0.0.0:3333 with mTLS')
    })
  }
}

2. Validate mTLS certificate and map to application identity

Create an authentication middleware that reads the client certificate and maps it to an internal principal. Do not derive access tokens from the certificate; instead, treat the certificate as the source of truth for identity within the application.

// start/kernel.ts
import { HttpContextContract } from '@adonisjs/core/http'
import { Exception } from '@adonisjs/core'

export default class AuthMiddleware {
  public async handle(ctx: HttpContextContract, next: () => Promise<void>) {
    const request = ctx.request
    // The raw cert is available via the socket in Node.js; this is simplified.
    const cert = request.socket.getPeerCertificate?.()
    if (!cert || Object.keys(cert).length === 0) {
      throw new Exception('mTLS certificate required', 401, 'E_MTLS_REQUIRED')
    }

    // Map certificate fingerprint or subject to user/app identity
    const subject = cert.subject
    const principalId = this.resolvePrincipalFromCert(subject)

    // Attach principal to context for downstream use
    ctx.auth = { type: 'mtls', principalId }
    await next()
  }

  private resolvePrincipalFromCert(subject: any): string {
    // Implement lookup: e.g., CN to user ID or API client ID
    return subject.CN || 'unknown'
  }
}

3. Avoid echoing tokens in responses and logs

Ensure that error handlers and logging never include authorization headers or token values. Use a centralized error handler that scrubs sensitive fields:

// start/handlers/error.ts
import { ExceptionHandler } from '@adonisjs/core/build/standalone'

export default class ErrorHandler extends ExceptionHandler {
  public handle(error: any, ctx: HttpContextContract) {
    // Never expose tokens or auth headers in error responses
    if (error.message.includes('token') || error.message.includes('authorization')) {
      ctx.response.status(500).send({ error: 'Internal server error' })
      return
    }
    // Generic logging without sensitive data
    logger.error(error.stack)
    super.handle(error, ctx)
  }
}

4. Prevent SSRF-induced token leakage to downstream services

If your service calls other APIs using mTLS, ensure that tokens meant for your clients are not forwarded. Validate and sanitize any user-supplied URLs and avoid passing incoming authorization headers to outbound requests.

// Example: safe outbound request without forwarding client tokens
import axios from 'axios'
import https from 'https'

export async function callDownstream(url: string, clientCert: any) {
  const agent = new https.Agent({
    cert: clientCert.cert,
    key: clientCert.key,
    ca: clientCert.ca,
  })
  const response = await axios.get(url, { httpsAgent: agent })
  return response.data
}

5. Enforce strict CORS and response headers

Limit CORS origins and avoid exposing tokens to browser contexts unless necessary. Use HttpOnly, Secure cookies for session tokens when applicable, and set Content-Security-Policy headers to reduce injection risks that could lead to token exfiltration.

By combining mTLS for strong client authentication with disciplined token handling and robust error management, AdonisJS services can maintain a clear boundary between transport security and application-level authorization, minimizing the risk of token leakage.

Frequently Asked Questions

Does mTLS alone prevent token leakage in AdonisJS?
No. mTLS secures the transport and binds client identity at the certificate level, but tokens can still leak through application code, logs, error messages, or SSRF-induced outbound calls. Application-level controls are required.
How can middleBrick help detect token leakage risks in AdonisJS APIs?
middleBrick scans the unauthenticated attack surface and OpenAPI specs, checking for tokens echoed in responses, overly permissive CORS, debug endpoints, and SSRF-prone outbound calls, including LLM output exposure when relevant.