HIGH auth bypasshapimutual tls

Auth Bypass in Hapi with Mutual Tls

Auth Bypass in Hapi with Mutual Tls — how this specific combination creates or exposes the vulnerability

Mutual Transport Layer Security (mTLS) in Hapi relies on both the server and the client presenting valid certificates during the TLS handshake. When mTLS is configured but route-level authorization is not enforced, a misconfiguration can lead to an authentication bypass: the connection is considered cryptographically authenticated, but the server treats the request as unauthenticated because the certificate identity is not mapped to an authorization context.

In Hapi, this typically occurs when developers enable TLS with request certificates (requestCert: true) but skip validating the certificate subject or mapping it to roles/users. Without explicit verification, an attacker who can terminate or intercept a client-side certificate (for example, via a stolen certificate or a CA compromise) can send requests that reach authenticated routes because the TLS layer presents a valid certificate, yet the application logic skips authorization checks.

Another common pattern is using mTLS for transport-layer assurance while relying on custom headers for user identity. If these headers are trusted without re-verifying the certificate-bound identity, an attacker can inject headers to bypass the intended authentication. This is especially risky when combined with insecure default routes or catch-all handlers that do not validate authorization for "authenticated" connections.

Consider a Hapi server that uses the built-in TLS options and a simple route that checks for a user in the session but does not validate the certificate subject:

const Hapi = require('@hapi/hapi');
const fs = require('fs');

const init = async () => {
  const server = Hapi.server({
    port: 443,
    tls: {
      key: fs.readFileSync('server-key.pem'),
      cert: fs.readFileSync('server-cert.pem'),
      ca: [fs.readFileSync('ca-cert.pem')],
      requestCert: true,
      rejectUnauthorized: false // intentionally permissive for testing
    }
  });

  server.route({
    method: 'GET',
    path: '/account',
    options: {
      auth: false, // no built-in auth strategy applied
      handler: (request, h) => {
        // Vulnerable: assumes TLS client cert maps to a user
        return { message: 'Account data' };
      }
    }
  });

  await server.start();
};
init();

In this setup, an attacker with a valid client certificate can access /account without the server verifying which user the certificate belongs to. The TLS layer authenticates the connection, but the application does not enforce authorization based on certificate attributes (subject, issuer, or SAN). This is a classic Auth BOLA/IDOR vector enabled by mTLS when identity mapping is omitted.

middleBrick detects this class of issue by correlating TLS configuration hints (e.g., requests for client certs) with missing authorization checks on authenticated-looking routes. It flags findings such as missing scope/role validation, permissive TLS settings like rejectUnauthorized: false, and routes where authentication is disabled but TLS presents a certificate.

Mutual Tls-Specific Remediation in Hapi — concrete code fixes

Secure mTLS in Hapi requires both correct TLS settings and explicit identity-to-authorization mapping. You should enforce certificate validation on the server and validate the certificate subject or extended key usage before allowing access to protected routes.

First, ensure the server rejects unauthorized certificates and that you validate the peer:

const Hapi = require('@hapi/hapi');
const fs = require('fs');

const init = async () => {
  const server = Hapi.server({
    port: 443,
    tls: {
      key: fs.readFileSync('server-key.pem'),
      cert: fs.readFileSync('server-cert.pem'),
      ca: [fs.readFileSync('ca-cert.pem')],
      requestCert: true,
      rejectUnauthorized: true // enforce client certificate validation
    }
  });

  server.ext('onRequest', async (request, h) => {
    const credentials = request.socket.getPeerCertificate();
    if (!credentials || Object.keys(credentials).length === 0) {
      throw Boom.unauthorized('Client certificate required');
    }
    // Map certificate fields to user/roles, e.g., by subject CN or SAN
    const subject = credentials.subject;
    const username = extractUsernameFromCert(subject); // implement this mapping
    request.auth.credentials = { username, roles: determineRoles(username) };
    return h.continue;
  });

  server.auth.scheme('custom-cert', (server) => ({
    authenticate: (request, h) => {
      if (!request.auth.credentials) {
        return h.authenticated({ credentials: request.auth.credentials });
      }
      return h.unauthenticated(Boom.badImplementation('Missing credentials'));
    }
  }));
  server.auth.strategy('cert-auth', 'custom-cert');

  server.route({
    method: 'GET',
    path: '/account',
    options: {
      auth: 'cert-auth',
      handler: (request, h) => {
        // Now identity is verified via cert mapping
        return { message: 'Account data', user: request.auth.credentials.username };
      }
    }
  });

  await server.start();
};
init();

Second, avoid treating TLS authentication as equivalent to application-level authorization. Always map certificate attributes to roles or permissions and enforce those on a per-route basis. For finer control, validate extended key usage or SAN fields to ensure the certificate is intended for API access:

function extractUsernameFromCert(subject) {
  // Example: extract CN or a SAN email
  const match = subject.match(/CN=([^,]+)/);
  return match ? match[1] : null;
}

function determineRoles(username) {
  // Map to roles using directory lookup or claims
  return ['user'];
}

middleBrick’s scans highlight the gap between TLS configuration and route-level auth by cross-referencing OpenAPI security schemes with runtime behavior. It surfaces findings tied to mTLS misconfigurations, such as missing authorization checks and permissive certificate validation, and provides remediation guidance aligned with frameworks like OWASP API Security Top 10.

Related CWEs: authentication

CWE IDNameSeverity
CWE-287Improper Authentication CRITICAL
CWE-306Missing Authentication for Critical Function CRITICAL
CWE-307Brute Force HIGH
CWE-308Single-Factor Authentication MEDIUM
CWE-309Use of Password System for Primary Authentication MEDIUM
CWE-347Improper Verification of Cryptographic Signature HIGH
CWE-384Session Fixation HIGH
CWE-521Weak Password Requirements MEDIUM
CWE-613Insufficient Session Expiration MEDIUM
CWE-640Weak Password Recovery HIGH

Frequently Asked Questions

Can mTLS alone prevent authorization issues in Hapi?
No. mTLS provides transport-layer identity but does not enforce application-level authorization. You must map certificate attributes to users/roles and apply route-level auth checks to prevent Auth Bypass.
What should I validate from the client certificate in Hapi?
Validate that the certificate is issued by a trusted CA, reject unauthorized peers (rejectUnauthorized: true), and map fields like subject or SAN to identities and roles before allowing access to protected routes.