Arp Spoofing in Nestjs with Mutual Tls
Arp Spoofing in Nestjs with Mutual Tls — how this specific combination creates or exposes the vulnerability
Arp spoofing is a layer-2 attack where an attacker sends falsified ARP messages to associate their MAC address with the IP address of a legitimate host, such as a backend server running NestJS. When mutual TLS (mTLS) is used, the encryption and server authentication occur at the transport layer, but mTLS does not prevent an attacker on the same local network from intercepting traffic between a client and the NestJS server. The client may unknowingly send frames to the attacker’s MAC address after ARP manipulation, while the attacker forwards frames to the legitimate server, creating a man-in-the-middle scenario that can capture or alter unencrypted session data and metadata even though the application payload is TLS protected.
In a NestJS application using mTLS, the server requests and validates client certificates during the TLS handshake. This ensures that only clients with a trusted certificate can establish secure channels. However, mTLS operates at the transport layer and does not mitigate layer-2 attacks like ARP spoofing. An attacker on the same network segment can still redirect traffic to their machine. If the attacker terminates a separate TLS session with the server using their own credentials (if server authentication is not further constrained), or if the attacker simply relays traffic while observing metadata, the confidentiality and integrity guarantees of mTLS can be bypassed at the network level. This is particularly relevant in environments where clients and servers share the same local network, such as within a data center or through compromised Wi-Fi.
The combination of NestJS and mTLS can inadvertently expose services if network-level protections are absent. For example, a NestJS server configured with request client certificate verification will accept connections only from clients presenting valid certificates. Yet if an attacker spoofs ARP to position themselves between the client and server, the attacker’s machine can present a valid client certificate if it has access to one, or attempt to relay traffic without terminating TLS at the attacker node, depending on network setup and certificate deployment. Additionally, without additional network segmentation or host-based controls, the attacker may capture TLS-encrypted traffic and perform passive analysis or attempt to exploit implementation-level weaknesses, such as insecure cipher suites or improper certificate validation, within the NestJS application.
ARP spoofing does not directly compromise the TLS session keys negotiated under mTLS, but it undermines the trust model by breaking endpoint identity assumptions at the network layer. Attackers can inject, drop, or modify packets that are not protected by encryption beyond the TLS session establishment, potentially affecting metadata, headers, or unencrypted portions of communication. In NestJS, this may manifest as unexpected request routing, session fixation, or exposure of IP and port information that could be leveraged for further attacks. Therefore, while mTLS strengthens authentication and encryption between trusted endpoints, it must be complemented by network-level defenses to prevent ARP spoofing and ensure end-to-end integrity.
Mutual Tls-Specific Remediation in Nestjs — concrete code fixes
To mitigate ARP spoofing risks while using mutual TLS in NestJS, apply defense-in-depth measures that combine proper mTLS configuration with network and host hardening. The following code examples demonstrate how to configure mTLS in NestJS and integrate additional checks that reduce the impact of layer-2 attacks.
Mutual TLS setup in NestJS with explicit client certificate verification
The following example shows a basic HTTPS server in NestJS that requests and validates client certificates using the built-in https module and NestJS adapters.
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import * as fs from 'fs';
import * as https from 'https';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const httpsOptions: https.ServerOptions = {
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 httpsAdapter = app.getHttpAdapter().getInstance();
httpsAdapter.server = https.createServer(httpsOptions, app.getCallback());
await app.listen(443);
}
bootstrap();
In this configuration, requestCert: true asks clients to present a certificate, and rejectUnauthorized: true instructs the server to reject connections when the client certificate cannot be verified against the provided CA. This enforces mTLS and ensures that only clients with certificates signed by the trusted CA can establish a TLS session.
Additional hardening to reduce ARP spoofing impact
While mTLS protects the content of communication, you should also implement network-level protections to prevent ARP spoofing. For example, use static ARP entries on critical hosts where feasible, enable port security on switches to limit MAC address changes, and isolate sensitive services using VLANs or network segmentation. On the host running NestJS, ensure that the operating system’s ARP cache validation is enabled and consider tools like arpwatch to detect anomalies.
You can further strengthen identity verification by binding client certificate attributes to application-level identities and logging certificate details for each request. The following snippet demonstrates extracting and validating client certificate information within a NestJS interceptor.
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { Observable } from 'rxjs';
@Injectable()
export class CertValidationInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable {
const request = context.switchToHttp().getRequest();
const socket = request.socket as any;
const cert = socket.getPeerCertificate && socket.getPeerCertificate();
if (!cert || !cert.subject) {
throw new Error('Client certificate missing');
}
// Example validation: ensure certificate contains required SAN or OU
const allowedOrg = 'example-corp';
if (!cert.subject.OU?.includes(allowedOrg)) {
throw new Error('Invalid client certificate organization');
}
return next.handle();
}
}
This interceptor checks that a client certificate is present and validates organizational unit (OU) information, adding an additional layer of assurance that the connecting client is authorized beyond what mTLS alone provides. Combined with network controls, this reduces the window of opportunity for attackers leveraging ARP spoofing to insert unauthorized parties into the communication path.