Dns Rebinding in Koa with Mutual Tls
Dns Rebinding in Koa with Mutual Tls — how this specific combination creates or exposes the vulnerability
DNS rebinding is a client-side attack in which an attacker causes a victim’s browser to resolve a seemingly trusted hostname to an arbitrary IP address after the initial DNS lookup. In a Koa application protected with mutual TLS (mTLS), the server authenticates clients via client certificates. If the application’s hostname is pinned to a private IP via mTLS expectations (e.g., certificate common name or SAN checks), but DNS resolution is not strictly constrained, an attacker can manipulate DNS responses to redirect the victim’s browser to a different IP while the browser still presents the client certificate expected by the server.
With mTLS enabled, the TLS handshake succeeds because the client certificate is valid, but the application may incorrectly trust the hostname bound to the certificate without corroborating it against the actual request target. For example, if Koa uses the certificate’s subject to derive authorization decisions (e.g., subdomain-based access control or internal service routing), an attacker can re-bind the hostname to an internal service like 127.0.0.1 or another backend host. The victim’s browser sends requests with a valid client certificate, and the Koa server may treat these as legitimate, bypassing intended network-level segregation.
Because middleBrick scans the unauthenticated attack surface, it can detect indicators of insecure DNS handling and mTLS configuration issues without credentials. Findings may include weak hostname validation, missing certificate pinning at the application layer, or reliance on DNS for security decisions. Remediation focuses on ensuring that hostname verification is independent of DNS and enforced consistently within the application and TLS configuration.
Mutual Tls-Specific Remediation in Koa — concrete code fixes
To mitigate DNS rebinding in Koa with mTLS, enforce strict hostname verification independent of DNS and validate client certificates against expected identities. Below are concrete, working examples using the https module and koa with koa-ssl-compat or native TLS options.
Example 1: mTLS server with hostname verification using Node.js native tls
const fs = require('fs');
const https = require('https');
const Koa = require('koa');
const app = new Koa();
const server = https.createServer({
key: fs.readFileSync('server-key.pem'),
cert: fs.readFileSync('server-cert.pem'),
ca: fs.readFileSync('ca-cert.pem'),
requestCert: true,
rejectUnauthorized: true,
// Pin client certificate subject alternative names or common name
checkServerIdentity: (servername, cert) => {
const allowedCommonName = 'trusted-client.example.com';
const subject = cert.subject || {};
const cn = subject.CN || '';
if (cn !== allowedCommonName) {
return new Error(`Certificate CN mismatch: expected ${allowedCommonName}, got ${cn}`);
}
// Optionally verify SANs or custom extensions here
return undefined;
}
}, (req, res) => {
// Minimal handling; Koa will handle the request below if using koa-ssl-compat
res.writeHead(200);
res.end('OK');
});
// Integrate with Koa by piping through a custom adapter if needed
server.on('request', app.callback());
server.listen(8443, () => {
console.log('mTLS + DNS rebinding protected server on https://localhost:8443');
});
Example 2: Enforce hostname binding before routing in Koa middleware
const Koa = require('koa');
const app = new Koa();
// Middleware to validate request hostname against certificate identity
app.use(async (ctx, next) => {
const expectedHost = 'api.trusted.example.com';
const requestHost = ctx.request.hostname; // e.g., 'api.trusted.example.com'
if (requestHost !== expectedHost) {
ctx.status = 400;
ctx.body = { error: 'Hostname mismatch' };
return;
}
// Additional check: ensure no private IP rebound via DNS
const socketAddress = ctx.socket.remoteAddress;
if (socketAddress.startsWith('127.0.0.1') || socketAddress.startsWith('::1')) {
ctx.status = 403;
ctx.body = { error: 'Localhost access not allowed' };
return;
}
await next();
});
// Secure route
app.use(ctx => {
ctx.body = { message: 'Access granted' };
});
app.listen(3000, () => {
console.log('Koa listening on port 3000 — ensure TLS termination happens upstream with mTLS');
});
Operational guidance
- Pin expected client identities (CN or SANs) in server-side TLS options and reject mismatches.
- Do not rely solely on DNS for security checks; validate hostnames against certificate metadata and request metadata.
- Restrict source IP ranges where possible and avoid exposing sensitive endpoints on localhost or internal IPs when mTLS is used.