Skip to main content

Server-side request forgery (SSRF)

Need

Prevention of unauthorized server-side requests to arbitrary domains

Context

  • Usage of Java for building cross-platform applications and enterprise-level software
  • Usage of java.io.BufferedReader for reading text from a character-input stream
  • Usage of java.io.InputStreamReader for reading input streams in Java
  • Usage of java.net.URL for handling URL connections and data retrieval
  • Usage of javax.servlet.http.HttpServlet for handling HTTP requests in Java Servlet applications
  • Usage of javax.servlet.http.HttpServletRequest for handling HTTP requests in Java Servlets
  • Usage of HttpServletResponse for handling HTTP response in Java Servlets

Description

Non compliant code

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class SSRFServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse res) {
try {
String url = req.getParameter("url");
URL obj = new URL(url);
BufferedReader in = new BufferedReader(new InputStreamReader(obj.openStream()));
String inputLine;
StringBuffer response = new StringBuffer();

while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();

res.getWriter().write(response.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
}

The above code is an example of a Server-Side Request Forgery (SSRF) vulnerability. It is a servlet that takes a URL parameter from the HTTP request and makes a request to that URL. After that, it reads the response from the requested URL and writes it back to the HTTP response.

Here is how the vulnerability works:

  • The doGet method of the servlet is called when an HTTP GET request is made to the servlet.
  • The req.getParameter("url") line gets the value of the "url" parameter from the HTTP request. This value is controlled by the user, who can enter any URL.
  • The new URL(url) line creates a URL object from the user-controlled string. This URL object is used to make a request to the specified URL.
  • The obj.openStream() method opens a connection to the URL and returns an input stream for reading from that connection.
  • The following lines read the response from the URL and append it to a StringBuffer object.
  • The res.getWriter().write(response.toString()) line writes the response back to the HTTP response.

The vulnerability lies in the fact that the application server makes a request to an arbitrary URL specified by the user. An attacker can exploit this to make the server request a malicious URL, potentially leading to data exposure, denial of service, or other impacts.

Steps

  • Validate user input: Ensure that the 'url' parameter is properly validated and sanitized before using it to make requests.
  • Whitelist allowed domains: Maintain a whitelist of allowed domains that the server can make requests to. Validate that the user-provided URL belongs to the whitelist before making the request.
  • Use a proxy server: Instead of making direct requests to user-provided URLs, use a proxy server to make the requests on behalf of the application. This can help prevent SSRF attacks by restricting the domains that the proxy server can access.
  • Implement rate limiting: Limit the number of requests that can be made to external domains within a certain time frame to prevent abuse and mitigate the impact of SSRF attacks.
  • Implement strong access controls: Ensure that only authorized users or components can access the vulnerable code and make requests to external domains.
  • Monitor and log requests: Implement logging and monitoring mechanisms to detect and investigate any suspicious or malicious requests made through the vulnerable code.
  • Keep software up to date: Regularly update the software and libraries used in the application to ensure that any known vulnerabilities are patched.

Compliant code

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.Arrays;
import java.util.List;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class SSRFServlet extends HttpServlet {
private static final List<String> WHITELISTED_DOMAINS = Arrays.asList("http://example.com", "http://example2.com");

protected void doGet(HttpServletRequest req, HttpServletResponse res) {
try {
String url = req.getParameter("url");
URL obj = new URL(url);

// Validate the URL
if (!WHITELISTED_DOMAINS.contains(obj.getHost())) {
throw new IllegalArgumentException("Invalid URL");
}

BufferedReader in = new BufferedReader(new InputStreamReader(obj.openStream()));
String inputLine;
StringBuffer response = new StringBuffer();

while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();

res.getWriter().write(response.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
}

The updated code includes a whitelist of allowed domains. When a GET request is made, the URL provided by the user is checked against this whitelist. If the URL's host is not in the whitelist, an IllegalArgumentException is thrown, and the request is not processed.

This approach prevents Server-Side Request Forgery (SSRF) by ensuring that the server only makes requests to trusted domains. It is a simple and effective way to mitigate the risk of SSRF attacks.

However, this is just one layer of defense. To further secure your application, consider implementing additional measures such as rate limiting, strong access controls, and regular software updates. Also, monitor and log requests to detect and investigate any suspicious activity.

References