Dns Rebinding with Jwt Tokens
How Dns Rebinding Manifests in Jwt Tokens
Dns Rebinding attacks exploit the time gap between DNS record expiration and token validation to bypass security controls. In Jwt Tokens implementations, this manifests when tokens are validated against domain names rather than IP addresses, allowing attackers to hijack the validation process.
The attack works by registering a domain that resolves to the attacker's IP, obtaining a valid Jwt Tokens, then changing the DNS record to point to the victim's server. When the Jwt Tokens is validated, the system checks the domain name (which the attacker controls) rather than the actual IP address, creating a window where the token appears valid but is being used against the wrong service.
// Vulnerable Jwt Tokens validation - domain-based checking
const validateJwt = (token, domain) => {
const payload = jwt.verify(token, process.env.JWT_SECRET);
if (payload.iss !== domain) {
throw new Error('Invalid issuer');
}
return payload;
};This implementation is vulnerable because it trusts the domain name provided in the token's iss claim without verifying the actual network connection. An attacker can rebind the DNS to point to a different service and still pass validation.
Another manifestation occurs in cross-origin token exchange scenarios. When Jwt Tokens are exchanged between services using domain names, DNS rebinding can intercept the exchange:
// Vulnerable cross-service token exchange
async function exchangeToken(sourceToken, targetDomain) {
const response = await fetch(`https://${targetDomain}/exchange`, {
headers: { Authorization: `Bearer ${sourceToken}` }
});
return response.json();
}If targetDomain is resolved once and cached, an attacker can rebind the DNS after the initial resolution but before the token is exchanged, intercepting sensitive Jwt Tokens.
Jwt Tokens-Specific Detection
Detecting Dns Rebinding in Jwt Tokens implementations requires examining both the token validation logic and the network layer where tokens are exchanged. The key indicators include:
- Domain name validation without IP address verification
- Long DNS caching times that exceed typical token lifetimes
- Cross-origin token exchanges without additional verification
- Missing network-level security controls around token endpoints
middleBrick's Jwt Tokens-specific scanning identifies these vulnerabilities by analyzing the runtime behavior of token endpoints. The scanner tests for Dns Rebinding by:
- Obtaining a valid Jwt Tokens from the target service
- Monitoring DNS resolution patterns during token validation
- Testing token reuse across different IP addresses
- Analyzing cross-origin token exchange endpoints
The scanner reports findings with specific severity levels based on the potential impact. Here's an example of what middleBrick detects:
{
"dns_rebinding": {
"severity": "high",
"description": "Domain-based Jwt Tokens validation without IP verification",
"impact": "Attacker can hijack valid tokens by DNS rebinding",
"remediation": "Implement IP-based validation or use mutual TLS",
"affected_endpoints": ["/api/auth/validate", "/api/exchange/token"]
}
}middleBrick also tests for timing vulnerabilities where token validation occurs after DNS resolution, creating a race condition exploitable through rebinding attacks. The scanner measures the time between DNS resolution and token validation to identify potential attack windows.
Jwt Tokens-Specific Remediation
Securing Jwt Tokens against Dns Rebinding requires implementing network-level controls and validation mechanisms that verify both the domain and the actual connection. The most effective approach combines IP-based validation with mutual authentication.
// Secure Jwt Tokens validation with IP verification
const validateJwtSecure = (token, expectedDomain, expectedIp) => {
const payload = jwt.verify(token, process.env.JWT_SECRET);
// Verify domain matches
if (payload.iss !== expectedDomain) {
throw new Error('Invalid issuer domain');
}
// Verify IP matches the current connection
const clientIp = getClientIp(req); // Implement your IP detection
if (clientIp !== expectedIp) {
throw new Error('IP mismatch - potential DNS rebinding');
}
return payload;
};This implementation ensures that even if an attacker controls the DNS record, they cannot use the token unless they also control the network path to the expected IP address.
For cross-origin token exchanges, implement additional verification layers:
// Secure cross-origin token exchange
async function exchangeTokenSecure(sourceToken, targetDomain) {
// Verify target domain's current IP
const targetIp = await resolveDomain(targetDomain);
// Establish mutual TLS connection
const response = await fetch(`https://${targetDomain}/exchange`, {
headers: { Authorization: `Bearer ${sourceToken}` },
httpsAgent: createMutualTlsAgent()
});
// Verify response IP matches expected
if (response.ip !== targetIp) {
throw new Error('Response IP mismatch');
}
return response.json();
}Another critical remediation is implementing short TTL values for DNS records combined with immediate token revocation on IP changes. This reduces the window of opportunity for rebinding attacks:
// Token revocation on IP change
const checkTokenValidity = (token, previousIp) => {
const payload = jwt.decode(token, { complete: true });
// Check if token was issued for a different IP
if (payload.payload.ip && payload.payload.ip !== previousIp) {
throw new Error('Token issued for different IP - potential hijacking');
}
return true;
};For Jwt Tokens implementations using refresh tokens, ensure refresh token endpoints are protected against Dns Rebinding by implementing IP pinning and mutual authentication:
// Secure refresh token endpoint
app.post('/refresh', (req, res) => {
const refreshToken = req.body.refreshToken;
const clientIp = getClientIp(req);
// Verify refresh token was issued for this IP
const payload = jwt.verify(refreshToken, process.env.REFRESH_SECRET);
if (payload.ip !== clientIp) {
throw new Error('Refresh token IP mismatch');
}
// Issue new access token
const newToken = generateAccessToken(payload.sub, clientIp);
res.json({ token: newToken });
});