HIGH insufficient loggingadonisjsmutual tls

Insufficient Logging in Adonisjs with Mutual Tls

Insufficient Logging in Adonisjs with Mutual Tls — how this specific combination creates or exposes the vulnerability

Insufficient logging in AdonisJS when mutual TLS (mTLS) is in use can obscure critical security events and hinder incident response. mTLS requires both the client and server to present valid certificates, adding a strong authentication layer. However, if the application does not log key details of the mTLS handshake and per-request authorization outcomes, an attacker can operate with reduced risk of detection.

With mTLS, the client certificate is typically available in AdonisJS via the request object (e.g., request.socket.getPeerCertificate() in Node-based adapters or framework-provified helpers). Failing to log the certificate’s subject, issuer, validity period, and verification status means you lose visibility into who is connecting and whether their certificate was properly validated. This is especially important given real-world attack patterns such as stolen or compromised client certificates, and it aligns with findings from scans that flag insufficient logging as a high-severity issue under standards like OWASP API Top 10 and SOC2 controls.

Additionally, insufficient logging in the request/response lifecycle (e.g., not logging route, method, user identity extracted from the cert, status codes, and validation errors) prevents correlating mTLS authentication with business logic authorization. For example, if a certificate maps to a low-privilege identity but the logs do not record attempted privilege escalation actions (such as accessing admin routes), suspicious behavior may go unnoticed. This gap also complicates compliance reporting for frameworks such as PCI-DSS and HIPAA, which require audit trails for access to sensitive endpoints.

When using the middleBrick CLI (middlebrick scan <url>) or Web Dashboard, scans may surface findings tied to missing certificate and request logging, providing prioritized remediation guidance. The Pro plan can enable continuous monitoring to detect regressions in logging coverage over time, while the GitHub Action can fail builds if risk scores drop due to missing audit controls.

To address this, ensure your AdonisJS application logs essential mTLS metadata for every request: certificate details, validation outcome, authenticated identity, and the outcome of any authorization checks. Combine this with structured logging and retention policies so logs are actionable during investigations and audits.

Mutual Tls-Specific Remediation in Adonisjs — concrete code fixes

Implementing robust logging alongside correct mTLS setup in AdonisJS helps ensure both security and auditability. Below are concrete steps and code examples tailored for AdonisJS projects.

1. Configure mTLS in AdonisJS

AdonisJS does not manage TLS directly but runs on Node.js HTTP servers. You typically configure mTLS at the server or proxy layer (e.g., using https module or a reverse proxy like Nginx/Traefik). Ensure the server requests and verifies client certificates.

const fs = require('fs');
const https = require('https');
const { Ignitor } = require('@adonisjs/ignitor');

const server = https.createServer({
  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,
}, (req, res) => {
  // The AdonisJS app will handle the request
  // Ensure you pass req to AdonisJS lifecycle appropriately, e.g., via an adapter hook
});

server.listen(443, () => {
  console.log('HTTPS server with mTLS running on port 443');
});

Note: In production, you may terminate TLS at a load balancer or API gateway that enforces mTLS and forwards requests to AdonisJS over HTTP. In that case, ensure the gateway validates client certificates and forwards trusted headers (e.g., x-client-ssl-cert) for downstream logging.

2. Log mTLS certificate details in a request lifecycle hook

Use AdonisJS middleware or a request listener to extract and log certificate information for every incoming request. This helps correlate authentication with authorization decisions.

// start/hooks.ts or a dedicated middleware file
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'

export function logMtlsCertificate({ request, response }: HttpContextContract) {
  // If behind a proxy that handles mTLS, read forwarded cert from header
  const forwardedCert = request.header('x-client-ssl-cert')
  let certDetails = null

  if (forwardedCert) {
    // Parse PEM and extract subject/issuer (use a library like @fidm/x509)
    const { parseCertificate } = require('@fidm/x509')
    try {
      const parsed = parseCertificate(Buffer.from(forwardedCert, 'base64'))
      certDetails = {
        subject: parsed.subject, // e.g., { CN: 'alice', O: 'Example' }
        issuer: parsed.issuer,
        validFrom: parsed.valid_from,
        validTo: parsed.valid_to,
        fingerprint: parsed.fingerprint,
      }
    } catch (err) {
      certDetails = { error: 'Failed to parse certificate' }
    }
  } else {
    // For native Node HTTPS, use socket getPeerCertificate if available
    const socket = request.socket
    if (socket && typeof socket.getPeerCertificate === 'function') {
      const cert = socket.getPeerCertificate()
      certDetails = {
        subject: cert.subject,
        issuer: cert.issuer,
        valid_from: cert.valid_from,
        valid_to: cert.valid_to,
        fingerprint: cert.fingerprint,
      }
    }
  }

  // Structured log example using pino or similar
  request.log = request.log || {}
  request.log.mtls = {
    verified: !!(certDetails && !certDetails.error),
    details: certDetails,
  }

  // Ensure logs include route and method for correlation
  request.log.route = request.url()
  request.log.method = request.method()
}

Then invoke this in your start/hooks.ts or as a global middleware to run on each request.

3. Log authorization outcomes and sensitive actions

After mapping the certificate to an identity (e.g., via a database lookup using fields like serialNumber or subject.CN), log the result of any authorization checks, especially for admin or sensitive operations.

// Example inside a controller or policy
async updateSensitiveResource({ request, auth, log }: HttpContextContract) {
  const userIdentity = request.log.mtls.details?.subject?.CN
  const isAdmin = await Policy.getUserRole(userIdentity) === 'admin'

  if (!isAdmin) {
    log.warn({
      event: 'unauthorized_access_attempt',
      route: request.url(),
      method: request.method(),
      identity: userIdentity,
      required_role: 'admin',
    })
    throw response.unauthorized('Insufficient permissions')
  }

  log.info({
    event: 'sensitive_update',
    route: request.url(),
    method: request.method(),
    identity: userIdentity,
  })

  // Proceed with update
}

4. Use middleBrick for continuous validation

Run the middleBrick CLI or Web Dashboard to validate that your endpoints expose necessary logging and that mTLS is enforced. The Pro plan supports continuous monitoring and can alert you if logging or authentication configurations degrade.

5. Example full AdonisJS middleware integration

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

export default class MtlsLogger {
  public async handle({ request, response, log }: HttpContextContract, next: () => Promise) {
    log.mtls = log.mtls || {}
    // Populate log.mtls as shown earlier
    await next()
    // Optionally log response status for correlation
    log.info({
      event: 'request_complete',
      route: request.url(),
      method: request.method(),
      status: response.status,
    })
  }
}

These steps ensure that mTLS authentication is visible in logs, authorization decisions are recorded, and suspicious activity linked to specific certificates is detectable.

Frequently Asked Questions

How does AdonisJS expose the client certificate for logging when using mTLS?
When mTLS is terminated at a Node.js HTTPS server, the certificate is available via request.socket.getPeerCertificate(). When behind a proxy that handles mTLS, configure the proxy to forward the client certificate in a header such as x-client-ssl-cert, and parse it in AdonisJS using a library like @fidm/x509.
What should be logged to ensure sufficient logging with mTLS in AdonisJS?
Log the certificate subject, issuer, validity period, and verification status; the authenticated identity mapped from the certificate; the request route, method, and response status; and the outcome of any authorization checks, especially denied attempts. Use structured logging to enable correlation and auditability.