SSRF in a nutshell:
When a server-based application allows end-users to specify a URL that the server then fetches and processes, the application is susceptible to SSRF attacks. Examples of such features include Webhooks, Blog Import, or Profile picture update using URL.
Attackers can abuse this functionality by passing an internal URL that is inaccessible from the Internet but accessible from the application server’s local network.
This can lead to attackers accessing internal systems or cloud metadata endpoints, which can sometimes lead to full cloud compromise.
Fixing or preventing SSRF issues
✅ Don’t introduce an SSRF-susceptible functionality
The best way to mitigate risk is risk avoidance. If the functionality isn’t essential for business, don’t implement features that allow users to specify URLs that are fetched by the application backend.
If you have to add this feature, implement the below checks to prevent SSRF issues.
✅ Prevent bare IP addresses in user-supplied URLs
There is no real need for such features to allow users to enter IP address URLs and these can be safely blocked without any side effects.
It is extremely difficult to assess whether a bare IP address is safe to fetch in such a functionality. Even if you blocklist internal or metadata IPs, there are ways to get around this using octal IPs, decimal IPs, or IPv6. Implementing more checks leads to more headaches and more potential opportunities for failure.
I have never seen this implemented as a security measure in such functionalities, but I believe that this can eliminate a lot of headaches for developers.
✅ Strict Allow-listing
If the feature is supposed to fetch resources from a limited set of sites, an allowlist of hostnames should be created and the URL passed by the user should be checked against the list of allowlisted hostnames.
If allowlisting does not meet your business use case, then follow the below guidance.
✅ Verify IP by resolving the requested host
Resolve the hostname supplied. After resolution, block requests with the below IPs.
Block IPv4 IPs in the following ranges:
0.0.0.0 – 0.255.255.255
10.0.0.0 – 10.255.255.255
127.0.0.0 – 127.255.255.255
169.254.169.254
172.16.0.0 – 172.32.255.255
192.168.0.0 – 192.168.255.255
Block the following IPv6 IPs:
::1
0:0:0:0:0:0:0:1
✅ Prevent DNS Rebinding Attacks
Just ensuring that the resolved IP of a host does not match a blocklist is not enough. An attack called DNS rebinding can be used by attackers to bypass such checks.
Attackers configure DNS servers to alternate DNS responses between a public IP address and an internal IP address. This way, when the first request is sent to check whether the hostname corresponds to an allowed IP address, the DNS server responds with the allowed IP address.
However, in the subsequent request that is sent when actually fetching the resource, the DNS server responds with an internal IP address.
This can be prevented by forcing the HTTP client module to use the IP address resolved in the validation step.
This way, you can ensure that the HTTP request sent to fetch the resource is sent to the IP address that has already been validated instead of relying on the subsequent resolution.
✅ Only allow http:
scheme in the URL
This can be done to prevent attackers from providing schemes such as file:
that can be abused to trick the application into fetching local files. Or worse, protocols like gopher:
can be used to send system commands on open ports.
✅ Finally, don’t follow redirects
All the above checks may fail, if you allow the HTTP client to follow redirects. Redirects can be abused to cause the HTTP client to hit a restricted endpoint via a redirect.
Hope this helps you better secure your application code. If you have any questions or if you would like to discuss further, please feel free to get in touch via LinkedIn.