Dns Rebinding in Express with Basic Auth
Dns Rebinding in Express with Basic Auth — how this specific combination creates or exposes the vulnerability
DNS rebinding is an application-layer attack that manipulates DNS responses to make a victim’s browser believe a remote host is reachable at an IP address that is not publicly routable, such as 127.0.0.1 or a local network address. In Express applications that rely on HTTP Basic Auth, this combination can bypass authorization assumptions when the app trusts the Host header or origin without additional network validation.
Consider an Express service that uses Basic Auth to protect an administrative endpoint but also performs internal service discovery or makes requests to a hostname resolved via DNS. If the application resolves a hostname once and then assumes the IP remains stable, an attacker can serve a low-TTL DNS record that alternates between a public IP and a private IP. After the victim’s browser resolves the public address, the attacker responds with a rebinded private IP, causing the victim’s browser to send requests to an internal endpoint.
If the Express route relies on the original Host header or does not validate that the resolved IP matches an expected network zone, the Basic Auth credentials submitted to the public hostname may be reused in the rebinded request. While the credentials are technically sent to the public hostname, the rebinding can trick the client into sending subsequent requests—including those with Authorization headers—to internal IPs that the server may treat as local or trusted. This can expose internal services that were assumed to be inaccessible from the internet.
In a black-box scan, middleBrick checks for signs that an API permits DNS-based address manipulation by correlating endpoint responses with changes in resolved IPs and unexpected cross-zone routing. This includes testing whether responses include references to localhost or private ranges when the hostname resolves differently over time. By running parallel checks across the unauthenticated attack surface, the tool can identify endpoints where DNS rebinding may weaken the effectiveness of Basic Auth boundaries without requiring authentication.
Basic Auth-Specific Remediation in Express — concrete code fixes
Defending against DNS rebinding in Express with Basic Auth requires a combination of strict host validation, rejection of private IPs, and avoiding implicit trust in the Host header. Below are concrete, minimal examples that you can adapt to your service.
1) Basic Auth middleware with explicit credentials
Use a well-maintained middleware such as express-basic-auth and avoid manual parsing of the Authorization header. Hardcode expected credentials or integrate with a secure store, and avoid dynamic lookups based on request Host.
const auth = require('express-basic-auth');
const users = { 'admin': 'correct-horse-battery-staple' };
app.use('/admin', auth({
users: users,
challenge: true,
realm: 'Secure Area',
authorizationHeaderName: 'Authorization'
}));
app.get('/admin', (req, res) => {
res.send('Admin area');
});
2) Reject private IPs and enforce canonical hostname
Before processing a request, validate the resolved destination IP and enforce a strict allowlist of hostnames. This prevents the server from inadvertently routing requests to internal addresses during rebinding attempts.
const dns = require('dns').promises;
const net = require('net');
async function validateHost(hostname) {
const allowedHosts = new Set(['api.example.com', 'app.example.com']);
if (!allowedHosts.has(hostname)) {
throw new Error('Hostname not allowed');
}
const resolved = await dns.resolve(hostname, 'A');
const ip = resolved[0];
// Reject private and loopback ranges
if (net.isIP(ip) && (net.isPrivate(ip) || net.isLoopback(ip))) {
throw new Error('Resolved IP is private or loopback');
}
// Optional: enforce expected IPs or ranges
return ip;
}
app.use(async (req, res, next) => {
try {
await validateHost(req.headers.host);
next();
} catch (err) {
res.status(400).send('Invalid host');
}
});
3) Avoid relying on Host for routing or authorization decisions
Do not derive access control or routing logic purely from the Host header. Combine Basic Auth with route-level guards and ensure that internal service calls use fixed, validated endpoints rather than dynamic hostnames supplied by the client.
app.get('/api/config', async (req, res) => {
// Host validation already applied globally
const config = await fetch('https://internal-config.example.com/v1/settings', {
headers: { Authorization: 'Basic ' + Buffer.from('svc:bound-token').toString('base64') }
});
const data = await config.json();
res.json(data);
});
4) Enforce HTTPS and HSTS
Serve your application over HTTPS and include HTTP Strict Transport Security headers to reduce the risk of protocol downgrade or host manipulation. This complements Basic Auth by protecting credentials in transit and making tampering more difficult.
app.use((req, res, next) => {
if (req.headers['x-forwarded-proto'] !== 'https') {
return res.redirect('https://' + req.headers.host + req.url);
}
next();
});
app.use(hsts({
maxAge: 31536000,
includeSubDomains: true,
preload: true
}));