spring-projects/spring-ai · critical · java.lang.SecurityException
Host '' resolves to a blocked internal address:
Error message
Host '' resolves to a blocked internal address:
What it means
MediaFetcher's custom DNS resolver wraps the system resolver and, immediately after resolving the media URL's host, checks every returned InetAddress against URLValidator.isBlockedAddress. If any resolved address is internal (loopback/private/link-local), resolution itself is aborted with this SecurityException so the request never proceeds to a connection. Like the other checks here it is a defense against SSRF via DNS.
Source
Thrown at models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/api/MediaFetcher.java:311
/**
* DNS resolver that rejects hostnames resolving to internal addresses. Acts as an
* early-rejection layer for hostname-based requests, complementing the socket-level
* check in {@link SsrfBlockingPlainSocketFactory} and
* {@link SsrfBlockingSSLSocketFactory} which covers raw IP literals that skip DNS
* resolution entirely.
*/
private static final class SsrfSafeDnsResolver implements DnsResolver {
@Override
public InetAddress[] resolve(String host) throws UnknownHostException {
InetAddress[] addresses = SystemDefaultDnsResolver.INSTANCE.resolve(host);
for (InetAddress address : addresses) {
if (URLValidator.isBlockedAddress(address)) {
// Throw SecurityException (RuntimeException) rather than
// UnknownHostException so it propagates through Spring RestClient
// without being wrapped in ResourceAccessException.
throw new SecurityException(
"Host '" + host + "' resolves to a blocked internal address: " + address.getHostAddress());
}
}
return addresses;
}
@Override
public String resolveCanonicalHostname(String host) throws UnknownHostException {
return SystemDefaultDnsResolver.INSTANCE.resolveCanonicalHostname(host);
}
}
}
View on GitHub (pinned to 98a7beda4f)
Solutions
- Point the media URL at a host that resolves to public addresses (public S3 bucket/CDN endpoint).
- If the media is internal, download it yourself with your own authorized client and pass the bytes inline to Media instead of a URL.
- Check DNS with dig/nslookup: if the host unexpectedly resolves to private IPs, correct the DNS record or use the correct public hostname.
- In test environments, host the media on an externally resolvable endpoint or embed it inline rather than using loopback URLs.
Example fix
// before
Media media = new Media(MimeTypeUtils.IMAGE_JPEG, new URL("http://internal-artifacts.corp/media/cat.jpg"));
// after: inline bytes fetched by your own internal client
byte[] bytes = artifactsClient.download("/media/cat.jpg");
Media media = new Media(MimeTypeUtils.IMAGE_JPEG, bytes); Defensive patterns
Strategy: validation
Validate before calling
// Java: mirror the library's DNS-level check before submitting the URL
InetAddress[] addrs = InetAddress.getAllByName(mediaUrl.getHost());
for (InetAddress a : addrs) {
if (a.isLoopbackAddress() || a.isLinkLocalAddress() || a.isSiteLocalAddress() || a.isAnyLocalAddress()) {
throw new IllegalArgumentException("Host " + mediaUrl.getHost() + " resolves to internal address " + a.getHostAddress());
}
} Try / catch
try {
model.call(prompt);
} catch (SecurityException e) {
if (e.getMessage().contains("resolves to a blocked internal address")) {
// treat as rejected URL input; surface to user, no retry
} else { throw e; }
} Prevention
- Check the host's DNS resolution (dig/nslookup) when testing media URLs from non-production networks.
- Use public hostnames for media; avoid internal-only DNS names that resolve to private IPs.
- Audit /etc/hosts and split-horizon DNS entries that could map test domains to loopback or private addresses.
- Pre-fetch internal media in your own service and submit bytes inline instead of URLs.
When it happens
Trigger: fetch() of a media URL whose hostname's DNS A/AAAA records include (or resolve solely to) a blocked internal address — checked in resolve() before any socket is opened.
Common situations: Split-horizon DNS where an internal name resolves to 10.x/192.168.x addresses; running the app inside a cluster where public-looking hostnames resolve to private IPs; localhost aliases or /etc/hosts entries mapping test domains to 127.0.0.1; stale DNS records repointed to private infrastructure.
Understand the failure class
Background: "Invalid URL" / "URL cannot be empty": fix the malformed or missing URL behind request-construction failures — this error's family across 50 libraries.
Related errors
- Connection to blocked internal address rejected for host ''
- URL host '' resolves to a blocked internal address:
- Host '' is not in the allowed hosts list. Configure MediaFet
- Failed to resolve host:
- URL is not valid under strict validation rules:
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/8aebefefa6cf385b.
Report an issue: GitHub.