more SSRF protection regarding IPv6

This commit is contained in:
Athou
2026-08-11 07:46:02 +02:00
parent ba454322db
commit 76a92ea44c
2 changed files with 76 additions and 2 deletions

View File

@@ -7,6 +7,8 @@ import com.google.common.net.HttpHeaders;
import inet.ipaddr.IPAddress;
import inet.ipaddr.IPAddressNetwork;
import inet.ipaddr.IPAddressString;
import inet.ipaddr.ipv4.IPv4Address;
import inet.ipaddr.ipv6.IPv6Address;
import jakarta.inject.Singleton;
@@ -137,8 +139,38 @@ public class HttpClientFactory {
}
private static boolean isLocalAddress(InetAddress address) {
IPAddress ip = new IPAddressNetwork.IPAddressGenerator().from(address);
return ip.isLocal() || ip.isLoopback() || ip.isMulticast() || CGNAT_RANGE.contains(ip);
return isLocalAddress(new IPAddressNetwork.IPAddressGenerator().from(address));
}
private static boolean isLocalAddress(IPAddress ip) {
if (ip.isLocal() || ip.isLoopback() || ip.isMulticast() || CGNAT_RANGE.contains(ip)) {
return true;
}
if (!ip.isIPv6()) {
return false;
}
// IPv6 transition mechanisms embed an IPv4 address that must be validated too, otherwise
// they could be used to smuggle a blocked IPv4 target past the IPv6-only checks above
IPv6Address ipv6 = ip.toIPv6();
if (ipv6.isIPv4Mapped() || ipv6.isIPv4Compatible() || ipv6.isWellKnownIPv4Translatable()) {
// IPv4-mapped (::ffff:x.x.x.x), IPv4-compatible (::x.x.x.x) and NAT64
// (64:ff9b::/96, RFC 6052) addresses all embed the IPv4 address in the lowest 32 bits
return isLocalAddress(ipv6.getEmbeddedIPv4Address());
}
if (ipv6.is6To4()) {
// 6to4 (2002::/16, RFC 3056) embeds the IPv4 address in bits 16-47
return isLocalAddress(ipv6.get6To4IPv4Address());
}
if (ipv6.isTeredo()) {
// Teredo (2001::/32, RFC 4380) embeds the IPv4 address in the lowest 32 bits,
// obfuscated with a bitwise complement
IPv4Address obfuscated = ipv6.getEmbeddedIPv4Address();
return isLocalAddress(new IPv4Address(~obfuscated.intValue()));
}
return false;
}
private record BlockLocalAddressesDnsResolver(DnsResolver delegate) implements DnsResolver {