Server-Side Request Forgery (SSRF) remains one of the most critical vulnerabilities in distributed cloud architectures. Naïve regular expressions fail to protect internal networks against DNS rebinding, IP encoding obfuscation, and URL parser differentials between validation middleware and HTTP fetch clients.
Modern applications frequently fetch external webhook URLs, import remote avatar images, or unfurl preview links. When backend services fetch user-supplied URLs without isolated network routing, attackers can pivot through the application server to query internal cloud metadata services (e.g. 169.254.169.254), Kubernetes etcd clusters, or unauthenticated internal microservices.
CWE-918: Server-Side Request Forgery (SSRF) • RFC 1918 / 6890: Special-Purpose IP Address Registries • Attack Vectors: DNS Rebinding (TOCTOU), Octal/Hex IP notation, Alternative Localhost Bindings
Why Regular Expressions Fail
Developers routinely attempt to validate URLs using regex patterns like /^https?:\/\/[a-z0-9.-]+/i. These patterns fail because IP addresses can be expressed in multiple valid formats that standard URI parsers resolve directly to private network interfaces:
| Input Encoding | Standard Representation | Resolution Target |
|---|---|---|
http://2130706433 |
Dword (Decimal IP) | 127.0.0.1 (Localhost) |
http://0177.0.0.1 |
Octal IP Encoding | 127.0.0.1 (Localhost) |
http://0x7f000001 |
Hexadecimal IP | 127.0.0.1 (Localhost) |
http://[::ffff:127.0.0.1] |
IPv4-mapped IPv6 | 127.0.0.1 (Localhost) |
http://0.0.0.0 / http://[::] |
Unspecified Address | Binds to local machine interface on Linux |
Validating a domain via DNS lookup before issuing an HTTP request leaves an exploit window for DNS Rebinding: the attacker's DNS server responds with a public IP during the validation check (TTL: 0s) and immediately resolves to 169.254.169.254 when the HTTP client executes the actual request.
Zero-Trust Validation Architecture
To securely resolve external URLs, validation and request dispatch must be atomic. The socket connection itself must inspect resolved IP addresses before the HTTP handshake commences:
import http from 'http';
import https from 'https';
import ipaddr from 'ipaddr.js';
import dns from 'dns/promises';
// Prohibited IP ranges (RFC 1918, RFC 3927, Loopback, Cloud Metadata)
const BLOCKED_RANGES = [
'unspecified',
'broadcast',
'linkLocal',
'loopback',
'private',
'reserved',
];
export async function validateIpAddress(ip: string): Promise<boolean> {
try {
const addr = ipaddr.parse(ip);
const range = addr.range();
if (BLOCKED_RANGES.includes(range)) {
return false;
}
// Explicit block for Cloud Provider Metadata (169.254.169.254)
if (addr.toNormalizedString() === '169.254.169.254') {
return false;
}
return true;
} catch {
return false;
}
}
export async function safeResolveHost(hostname: string): Promise<string> {
const addresses = await dns.lookup(hostname, { all: true });
for (const record of addresses) {
const isSafe = await validateIpAddress(record.address);
if (!isSafe) {
throw new Error(`Access to restricted address range is prohibited: ${record.address}`);
}
}
return addresses[0].address;
}
For further security engineering advisories and deep-dive technical breakdowns, refer to the security research papers indexed on tirup.in.
- Never rely on client-side or regex-based domain validation for backend HTTP dispatches.
- Enforce custom socket-level IP validation to prevent DNS rebinding TOCTOU race conditions.
- Block both IPv4 and IPv6 loopback, link-local, private, and cloud metadata address ranges.
- Disable HTTP redirect following or re-validate every hop in the redirection chain.