Server-Side Request Forgery attacks involve getting a target server to perform requests on our behalf. Rather than covering some great material already published, this post will be to introduce a new PHP package designed to help prevent these sort of attacks.
Protections
To protect our scripts from being abused in this way, we simply validate any URL or file path being passed to functions which can send requests. Of course, this is easier said than done.
The first step is to validate the provided scheme (and port if specified). This is to stop requests to PHP’s extra protocols (php://
, phar://
) which would let an attacker read files off of the file system.
The second is to validate the URL itself. This is to make sure that someone isn’t requested a blacklisted domain (such as https://jira.fin1te.net
), or a private/loopback IP (such as 127.0.0.1
). You should also resolve any domain names to their IP addresses, and validate these to make sure someone doesn’t use a DNS entry pointing to an invalid IP.
Lastly, any redirects which cURL would normally handle should be caught, and the URL specified in the Location
header validated using the above steps.
Putting this all together, we get SafeCurl.
SafeCurl
SafeCurl has been designed to be a drop in replacement for the curl_exec
function in PHP. Whilst there are other functions in PHP which can be used to grab the contents of a URL (file_get_contents
, fopen
, include
), curl_exec
is the most popular. In future versions, support for other functions will be added.
To use SafeCurl, simply call the SafeCurl::execute
method where you’d usually call curl_exec
, wrapping everything in a try/catch
block.
By default, SafeCurl will only allow HTTP or HTTPS requests, to ports 80, 443 and 8080, which don’t resolve to a private/loopback IP address.
If you wish to specify additional options, instantiate a new Options
object and pass in your custom rules. Domains are accepted in regular expression format, and IPs in CIDR notation.
More usage information is available on the Github project. If you find any issues please raise them, or better yet, submit a pull request.
If you manage to find a way of bypassing it completely, then please participate in the bounty.
Bounty (Capture the Bitcoins)
In order to give SafeCurl a real-world test, I’ve hosted a demo site, which lets you try out the different protections.
The document root contains a Bitcoin private key, with 0.25BTC contained within. This file is only accessible from localhost, so if you do bypass it, grab the file and the Bitcoins are yours.
The source code for the site is also available, if you’re interested.
For more information see the Bounty page.