16

The application lets users specify a URL for their profile picture. It fetches the data from the URL and saves it on the server. However, the app is vulnerable to server-side request forgery (SSRF) - you can specify URLs like file:///etc/passwd and also access local HTTP services like http://localhost:8080/.

What's the best way to fix this? Some ideas I had:

  • Restrict the URL protocol to HTTP and HTTPS.
  • DNS lookup the host name and check it's not a local address.
  • Disable redirects.

This seems a good start - although there's a TOCTOU issue with the DNS lookup (may be ok in practice, as local DNS will cache).

An alternative idea I had was to place a proxy in a restricted network segment, where it can only see the Internet, and send requests through that.

I did research this online, but there's not much information. This article is pretty good.

6
  • 2
    Using a segmented proxy server is a good idea. I answered a similar question here. DNS caching can be circumvented by building the request manually (i.e. explicitly send the request to the resolved IP address and set host header/the host used for SNI manually). Commented May 18, 2016 at 18:26
  • Thanks @SilverlightFox. Any tip on how to explicity send a request to specific resolved IP? I couldn't see how with java.net.URL
    – paj28
    Commented May 18, 2016 at 20:30
  • See this answer - you can set the IP address as the host, then manually set the Host header. Of course for HTTPS connections you'll have to check that the API automatically uses your set Host header for the SNI sent during handshaking. Commented May 19, 2016 at 10:50
  • Would a whitelist/blacklist solution work, or is it too restrictive? For example, you could whitelist several known image sharing websites. Local servers (including localhost) in your company's domain could be blacklisted with the exception of say one server designated to share images.
    – HTKLee
    Commented Jun 18, 2016 at 9:22
  • Also since your application is fetching images from other servers, a rate limiter to ensure the app doesn't flood the target URLs with requests could be important. You don't want your users, whether by accident or otherwise to launch a DoS attack against the URL they specify using your app.
    – HTKLee
    Commented Jun 18, 2016 at 9:31

1 Answer 1

12
+50

This is the code I'm currently using:

if(!url.getProtocol().startsWith("http"))
    throw new Exception();
InetAddress inetAddress = InetAddress.getByName(url.getHost());
if(inetAddress.isAnyLocalAddress() || inetAddress.isLoopbackAddress() || inetAddress.isLinkLocalAddress())
    throw new Exception();
HttpURLConnection conn = (HttpURLConnection)(url.openConnection());
conn.setInstanceFollowRedirects(false);
conn.connect();
IOUtils.copy(conn.getInputStream(), out);

It enforces HTTP or HTTPS, forbids local addresses, and disables redirects. However, it still has the TOCTOU (time of check, time of use) flaw.

Edit (2022): The best solution these days is to use a library specifically designed to safely fetch untrusted URLs. For example, Advocate for Python. I don't have a link to a similar Java project to hand; if someone can add this, I would appreciate it!

1
  • According to the jdk sources of openjdk8 the is*Address functions always return false.
    – thi gg
    Commented Nov 9, 2020 at 16:50

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .