Guidelines

Server-Side Request Forgery

Server-Side Request Forgery vulnerabilities occur when a user is able to cause an application to make HTTP requests to an attacker-determined domain. If an application has access to private/internal networks, an attacker could also cause the application to make requests to internal servers. 

We’ll take a closer look at this with some examples to better understand what it looks like in action.

Consider an API that takes a URL from a user and generates a picture of the URL provided by the user, such as for a preview or export of a page. 

ts
let url = request.params.url;

let response = http.get(url);
let render = response.render();

return render.export();

Because the URL parameter is controlled by the user, it allows an attacker to simply access any URL they want. In some cases, depending on the library used to access the URL, it can even be local files using the ‘file://’ scheme.  

A common way of abusing an SSRF vulnerability, when hosted on AWS, is to use it to access the AWS Metadata API which can contain credentials for the AWS API. This can lead to further access to other AWS resources within the account which, as you can imagine, is not ideal.

Mitigation

Mitigating SSRF vulnerabilities can be very tricky at times, and depend heavily on what the code in question is trying to achieve. Depending on the requirements, there are different mitigations that can be put in place.

Avoid user-determined URLs

In some cases, you may be able to implement a feature in a way that doesn’t rely on a user providing an arbitrary URL. If this is at all a possibility, it's the most effective way of mitigating the risk of SSRF.

Minimize capabilities 

If you’re implementing a PDF export function, one might be inclined to simply use a headless browser and take a screenshot of the page. This isn’t always advisable, given the complexity of browsers and the sheer number of capabilities and attack surfaces that browsers expose.

It's important to use the right tool for the job at hand. In some cases, a simple HTTP client will be sufficient. There are always things that can be done to minimize the capabilities, and thus the attack surface/vectors available. For instance, in the case of an HTTP client:

  • Disable the following of redirects
  • Disable all schemes other than HTTPS

There are some pitfalls

Redirects & iframes

A common way of protecting against SSRF on private resources (IP addresses, or internal hostnames) is to parse the URL provided by a user and use a ‘deny-list’ to prevent access to sensitive resources.

It’s worth noting that this method isn’t effective in most cases, as it can be bypassed in scenarios where the client follows HTTP redirects, HTML/JavaScript redirects, or can render complex elements like iframes.

An attacker can provide a URL to a vulnerable application that hosts a page that redirects to a sensitive resource either through an HTTP 301/302, a HTML Meta redirect, setting the current URL with Javascript on load, or embedding an iframe showing an internal resource. This effectively bypasses any attempt at validating the original URL. 

DNS Rebinding
Another "type" of redirect can also be done through DNS. One common way of trying to prevent SSRF attacks is to do the following:

  1. Parse the URL provided
  2. Take the hostname, and do a DNS lookup
  3. Reject the URL if it resolves to an internal/private IP, or accept the URL if it is a public IP

This method isn’t really effective due to it being vulnerable to "DNS Rebinding". DNS Rebinding works because of the standard behavior of most network stacks (Such as those of Linux and Windows) when a TCP connection is closed by a remote host.
When a remote host forcefully closes a connection, it will attempt to reconnect after doing another DNS query to re-resolve the IP address. 

This allows an attacker to do the following:

  1. Create a DNS entry for `rebinding.attacker.com` with a public IP address with an open HTTP port with a very short TTL (Time-to-live)
  2. Submit the URL  (example: https://rebinding.attacker.com/) to the vulnerable application. It checks to make sure the resolved IP is not private, which it isn't, and then proceeds with the requested operation under the belief that it's safe
  3. Once the HTTP connection is made, the DNS entry for “rebinding.attacker.com” is changed to an internal IP address
  4. The HTTP connection is forcefully closed
  5. This now causes the application to re-resolve the IP address of “rebinding.attacker.com” which now points to an internal IP address
  6. Because the IP resolution protection has already occurred, and the re-resolution of the DNS entry and re-connecting all happens in the kernel, the application is not aware of the fact that the IP has now changed

This effectively bypasses the protection against providing URLs that resolve to private IP addresses. 

IPv4 vs IPv6

Another common way of bypassing any ‘deny-list’ is the use of IPv6. Because all IPv4 addresses can be expressed in terms of an IPv6 address, it’s often possible to bypass deny-lists by using an IPv6 address that also maps to an IPv4 address that one would want to access.