Brute Force Attack in Nestjs with Mutual Tls
Brute Force Attack in Nestjs with Mutual Tls — how this specific combination creates or exposes the vulnerability
A brute force attack targets authentication by systematically trying many credentials. In a NestJS application using mutual TLS (mTLS), the presence of client certificates changes the attack surface but does not eliminate password-based risks. With mTLS, the server validates a client certificate before the application layer processes the request. If the certificate validation is weak or misconfigured, an attacker may bypass certificate checks or target the application’s fallback or secondary authentication paths.
When mTLS is implemented but the application still accepts username/password login over the same endpoint, brute force attempts can shift to those credentials. Additionally, if certificate verification is performed after some processing or if errors in certificate validation are handled inconsistently, an attacker might learn information that aids enumeration. Rate limiting might be applied at the HTTP layer, but if it is not enforced per certificate identity or if multiple certificates are accepted, an attacker can cycle through accounts without triggering protections.
For example, consider a NestJS app that requires mTLS for most routes but allows unauthenticated access to a public endpoint that returns whether a user exists. An attacker can use that endpoint to enumerate valid users and then perform credential brute force against the login route. Even with mTLS, if session tokens or API keys are issued after certificate authentication without additional factors, compromised client certificates can enable unauthorized access that is hard to trace without proper monitoring.
The OWASP API Security Top 10 category Broken Object Level Authorization (BOLA) intersects with brute force when object-level permissions are not enforced consistently after mTLS authentication. A brute force attack here may not target passwords directly but can probe IDs to see whether a certificate-bound session can access resources it should not. This highlights the need to align certificate-based identity with application-level authorization and to enforce rate limits and monitoring on authentication endpoints.
Mutual Tls-Specific Remediation in Nestjs — concrete code fixes
To secure a NestJS application with mutual TLS and reduce brute force risks, enforce strict certificate validation, bind certificate data to application identity, and apply rate limiting and monitoring at the right layers. Below are concrete code examples illustrating these practices.
Strict mTLS setup with explicit verification
Configure an HTTPS server in NestJS that requires and verifies client certificates. Do not accept untrusted or self-signed CAs.
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import * as https from 'https';
import * as fs from 'fs';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
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,
};
await app.listen(3000, '0.0.0.0', () => {
console.log('HTTPS server with mTLS listening on port 3000');
});
}
bootstrap();
The rejectUnauthorized: true setting ensures that connections without a valid, trusted client certificate are rejected before reaching application code. Use a dedicated CA that you control to avoid trusting external or test certificates.
Extract and validate certificate identity in a guard
Map the client certificate to an application user or role to enforce authorization consistently. Do not rely solely on the presence of a certificate.
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { ClientCertificate } from './cert-types';
@Injectable()
export class CertAuthGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
const request = context.switchToHttp().getRequest();
const cert = request.socket.getPeerCertificate() as ClientCertificate;
if (!cert || !cert.subject) {
return false;
}
// Validate against an allowlist or mapped identity store
const fingerprint = this.fingerprint(cert);
return this.isAllowedCertificate(fingerprint);
}
private fingerprint(cert: ClientCertificate): string {
// Use a stable property available from the TLS session
return cert.fingerprint || this.computeFingerprintFromRaw(cert);
}
private isAllowedCertificate(fingerprint: string): boolean {
// Implement lookup against a secure store
return allowedFingerprints.has(fingerprint);
}
private computeFingerprintFromRaw(cert: ClientCertificate): string {
// Example: SHA256 fingerprint computation using crypto
const crypto = require('crypto');
return crypto.createHash('sha256').update(cert.raw).digest('hex');
}
}
Apply this guard to routes that require certificate-bound identity. Combine it with role claims extracted from certificate fields (e.g., OU or custom extensions) to implement fine-grained authorization.
Rate limiting tied to certificate identity
Prevent brute force attempts by applying rate limits per certificate identity rather than only by IP, which can be shared or NATed.
import { Injectable, NestMiddleware } from '@nestjs/common';
import * as rateLimit from 'express-rate-limit';
@Injectable()
export class CertificateRateLimitMiddleware implements NestMiddleware {
use(res: any, next: () => void) {
const limiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 100,
keyGenerator: (req: any) => {
const cert = req.socket.getPeerCertificate();
return cert.subject ? cert.subject.CN : req.ip;
},
handler: (req, res) => {
res.status(429).json({ message: 'Too many requests' });
},
});
limiter(res, undefined, next);
}
}
Ensure public endpoints that reveal user existence do not allow unauthenticated probing at scale; require mTLS or other strong authentication before returning detailed responses.
Monitoring and anomaly detection
Log certificate fingerprints alongside authentication attempts and monitor for repeated failures across usernames or accounts. This helps detect credential spraying even when mTLS is in place.