Broken Authentication in Nestjs with Mutual Tls
Broken Authentication in Nestjs with Mutual Tls
Mutual Transport Layer Security (mTLS) requires both the client and the server to present and validate certificates. In NestJS, enabling mTLS is typically done at the transport layer (for example with an Express or Fastify adapter) rather than solely within NestJS application code. When mTLS is configured incorrectly or when application-level authentication is not aligned with the mTLS trust boundary, broken authentication can occur.
One common misconfiguration is treating the presence of a client certificate as sufficient authentication without validating its details in application logic. For example, a server may accept any certificate signed by a trusted CA but fail to check the certificate’s subject, extended key usage, or mapped identity before authorizing access. This gap means that even with mTLS, an attacker who obtains a valid client certificate from a trusted CA can authenticate as any principal the server trusts, leading to Broken Authentication.
Another risk specific to NestJS applications is inconsistent enforcement of authentication. If some routes rely on mTLS-derived identity (e.g., reading the certificate subject from the socket) while others depend on bearer tokens or session cookies, authorization rules may diverge. An attacker can exploit these inconsistencies by choosing the weaker path. For instance, if mTLS is enforced at the ingress or adapter level but NestJS guards are missing or misconfigured, unauthenticated or under-authorized requests can reach endpoints that assume strong client identity.
Additionally, improper certificate validation in NestJS middleware or interceptors can weaken mTLS. If certificate revocation checks (CRL or OCSP) are omitted, a stolen or revoked client certificate may still be accepted. Similarly, failing to set a strict certificate chain validation can allow an attacker to present a certificate signed by a trusted CA but with an unexpected chain, bypassing intended access controls.
These issues map to common weaknesses such as CWE-287 (Improper Authentication) and CWE-295 (Improper Certificate Validation). They also intersect with OWASP API Top 10 controls, particularly authentication and authorization failures. Because mTLS shifts some security responsibilities to infrastructure, developers must ensure that NestJS application logic corroborates mTLS identity and enforces authorization consistently.
Mutual Tls-Specific Remediation in Nestjs
Remediation focuses on strict certificate validation, consistent identity extraction, and aligning mTLS with application-level authorization. Below are concrete, syntactically correct examples for a NestJS app using Express and Fastify adapters.
Express adapter with mTLS and identity validation
import { NestFactory } from '@nestjs/core';
import { ExpressAdapter } from '@nestjs/platform-express';
import * as https from 'https';
import * as fs from 'fs';
import { AppModule } from './app.module';
async function bootstrap() {
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,
// Ensure strict certificate validation
checkServerIdentity: (servername, cert) => {
// Example custom check: enforce hostname or SAN
if (!cert.subjectaltname || !cert.subjectaltname.includes(servername)) {
return new Error('Certificate hostname mismatch');
}
return undefined;
},
};
const app = await NestFactory.create(AppModule, new ExpressAdapter());
// Enforce mTLS at the adapter level
app.use((req, res, next) => {
const cert = req.socket.getPeerCertificate?.();
if (!cert || Object.keys(cert).length === 0) {
res.status(400).send('Client certificate required');
return;
}
// Validate certificate attributes in application logic
const subject = cert.subject;
// Example: ensure a specific organizational unit or role
if (!subject.OU?.includes('api-clients')) {
res.status(403).send('Unauthorized certificate OU');
return;
}
// Attach identity to request for downstream guards/auth providers
req.clientCert = { subject, serialNumber: cert.serialNumber };
next();
});
await app.listen(3000, () => console.log('HTTPS server with mTLS on 3000'));
}
bootstrap();
Fastify adapter with mTLS and role-based access
import { NestFactory } from '@nestjs/core';
import { FastifyAdapter } from '@nestjs/platform-fastify';
import * as fs from 'fs';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule, new FastifyAdapter({
https: {
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,
},
}));
// Fastify-level hook to validate client certificate and extract roles
app.addHook('onRequest', async (request, reply) => {
const socket = request.socket as any;
const cert = socket.getPeerCertificate && socket.getPeerCertificate();
if (!cert || Object.keys(cert).length === 0) {
throw new Error('Client certificate required');
}
// Validate extended key usage or custom OID for API access
const eku = cert.ext_key_usage || [];
if (!eku.includes('clientAuth')) {
throw new Error('Certificate not authorized for client auth');
}
// Map certificate fields to roles (example via SAN or custom OID)
const roles = [];
if (cert.subjectaltname) {
cert.subjectaltname.split(/[,\s]+/).forEach((entry) => {
if (entry.startsWith('role:')) roles.push(entry.slice(5));
});
}
request.user = { certificate: cert, roles };
});
await app.listen(3001);
}
bootstrap();
Authorization consistency
Ensure that NestJS guards or custom request-scoped auth providers use the identity derived from mTLS rather than a separate token when mTLS is the primary auth mechanism. For example, a custom AuthGuard can read req.clientCert or request.user and verify roles/scopes before allowing access.
Enable revocation checks where possible by loading CRLs and, if the platform supports it, performing OCSP validation at the infrastructure or custom middleware level. Avoid treating mTLS as a one-time setup; rotate CA and client certificates as part of a lifecycle process and reflect changes in NestJS configuration and identity mapping logic.
Related CWEs: authentication
| CWE ID | Name | Severity |
|---|---|---|
| CWE-287 | Improper Authentication | CRITICAL |
| CWE-306 | Missing Authentication for Critical Function | CRITICAL |
| CWE-307 | Brute Force | HIGH |
| CWE-308 | Single-Factor Authentication | MEDIUM |
| CWE-309 | Use of Password System for Primary Authentication | MEDIUM |
| CWE-347 | Improper Verification of Cryptographic Signature | HIGH |
| CWE-384 | Session Fixation | HIGH |
| CWE-521 | Weak Password Requirements | MEDIUM |
| CWE-613 | Insufficient Session Expiration | MEDIUM |
| CWE-640 | Weak Password Recovery | HIGH |