Description
SSRF occurs when a server makes a request using a supplied URL or similar input without sufficiently checking that the destination is authorized.
Potential impact
- Disclosure of sensitive information reachable by the server
- Unauthorized operations or code execution through exposed services or additional vulnerabilities
- Access to internal networks or bypass of controls that trust the server's network position
Remediation
- Where possible, accept a server-owned endpoint identifier instead of a URL and map it to a fixed URI.
- Restrict the server's outbound connections to required destinations. Block access to internal or metadata addresses that the application does not need.
- Explicitly disable redirects so the HTTP client cannot follow an approved endpoint to an internal address or another host.
Examples
Before
java
RestTemplate restTemplate = new RestTemplate();
String url = request.getParameter("url");
String result = restTemplate.getForObject(url, String.class);
After
In this Java 11 or later example, replace the URIs with actual administrator-approved destinations. DNS and outbound routing must also be trusted. Enforce a separate deadline for consuming the streaming response body.
java
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
public final class SafeEndpointClient {
private static final Map<String, URI> ENDPOINTS = Map.of(
"profile", URI.create("https://trusted.com/api/profile"),
"status", URI.create("https://example.com/api/status"));
private static final HttpClient CLIENT = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(3))
.followRedirects(HttpClient.Redirect.NEVER)
.build();
public String fetch(HttpServletRequest request) throws IOException, InterruptedException {
URI target = ENDPOINTS.get(request.getParameter("endpoint"));
if (target == null) {
throw new IllegalArgumentException("Unknown endpoint");
}
HttpRequest outbound = HttpRequest.newBuilder(target)
.timeout(Duration.ofSeconds(5))
.GET()
.build();
HttpResponse<InputStream> response =
CLIENT.send(outbound, HttpResponse.BodyHandlers.ofInputStream());
if (response.statusCode() >= 300 && response.statusCode() < 400) {
response.body().close();
throw new IOException("Redirects are not allowed");
}
try (InputStream body = response.body()) {
byte[] data = body.readNBytes(1024 * 1024 + 1);
if (data.length > 1024 * 1024) {
throw new IOException("Response is too large");
}
return new String(data, StandardCharsets.UTF_8);
}
}
}
The first example requests the client's URL directly. The second lets input select only an endpoint key; the URI comes from a fixed mapping and redirects are disabled. It also sets connection and request timeouts and a response-size limit.
Related CVEs
- CVE-2023-32786: Prompt injection in an LLM framework enables SSRF and downstream content injection (CWE-1427/CWE-918)
- CVE-2021-26855: SSRF in a mail server, listed in CISA KEV
- CVE-2021-21973: SSRF in a cloud platform, listed in CISA KEV