> How did I end up here?
Whenever a user is sent to some unexpected (and perhaps malicious) third-party site, an Unvalidated Redirection is said to have occurred. Though it is also known by quite a few other names: unvalidated redirect, open redirection, unvalidated forward, and so on.
Imagine a large website that would like to know how users exit their site. Perhaps they have links to Twitter and YouTube, and would like to have some metrics on how much traffic they’re redirecting.
They could set it up as:
So if they want to redirect someone to YouTube, they could link to:
The code would probably look something like:
<?php //Take value of GET parameter url $url = $_GET['url']; //Redirect the user to url header(301,$url); //Log the redirection for metrics etc ?>;
I’m sure you can already spot the problem here. What if we set something malicious as the URL? If we have http://example.com/redirect.php?url=http://evil.com/ as the URL, then the page would redirect to evil.com.
Makes sense. Well… What happens makes sense. We supply the link, the page sends the web browser to that page. D’uh. So what? Doesn’t matter.
Well, it does matter. Especially when it comes to phishing. You click a link going to your bank, and you end up on a page very similar-looking to that of your bank… but in reality, you were redirected to a phishing site.
As a user it’s rather hard to notice until you’ve already been redirected.
How often do you check the entire URL?
Users don’t usually look at the entire URL. We’re lazy. We check the domain (because we know phishing is a thing), and then we stop. Besides, the name of the parameter isn’t obvious, and if someone is out to get you, then “evil.com” will most likely not be the domain of choice. It would most likely be: /redirect.php?url=http://example.com.totallynotevil.com
Perhaps you’re notorious when it comes to reading the entire link… Sure. Well, what if your web browser (or email client, etc) only show you the domain? Then you have no idea what will happen. Bummer.
Therefore, always double-check the URL before entering any sensitive information on the page (such as login credentials).
Fixing the problem
Step one: Don’t directly determine the destination based on the user input. If you just forward the user to the value of $url, then you’re doing a bad thing. Instead:
A. Refer an index
Use “url=1” or “url=15”, and then map these IDs to the destination. Is your Malicious destination not in the list? Good.
Sometimes you can’t keep track of all the destinations. Perhaps they are dynamically generated, and you just can’t manage it for some reason. In that case, keep a list of trusted domains, and use it as a whitelist. You need to get it right and make sure you cant circumvent the filter.
C. What not to do: blacklist
A broken approach can never be fixed using blacklists. Removing one domain or a specific pattern won’t stop anyone for long. Keep a mapped list, or use a strict whitelist.
See a URL being passed as a parameter? You should test what happens if that URL is modified to something else.
Note that the same goes for relational paths. If “foo/bar/account.html” is passed as a parameter, try and see what happens if you change that to “http://example.com”.
Vulnerable code is vulnerable, there is no way to get around that. Fix the core problem and be done with it… right? Yes, though sometimes it’s not (for one reason or another) possible to actually fix it. Sometimes you need a quick workaround.
Well, I can’t help you with that, but I can give you a heads up with some common problems when it comes to filtering or blocking:
A. Filter is case-sensitive
Of course you should filter/block http://, hTTp://, and HTTP://.
B. Filter is protocol-dependent
We can use both HTTPS:// and HTTP://, so make sure to filter both.
C. Filter explicitly expects protocol
Did you know “//” refers to the current protocol scheme? So a link to “//example.com” translates to “link to example.com using HTTP if the current page uses HTTP, and HTTPS if the current page uses HTTPS”.
D. Expecting domain
Okay, so we can’t do “evil.com”. But what about an IP address?
E. Expecting IP address in octal format
Okay, so you got 192.168.10.5 all covered. Did you know you can link to http://3627732484/ (www.google.com, 126.96.36.199)? Hah! Madness!
F. Relative links
If all of the above is taken care of, make sure that there are no relative links on any of the allowed domains that can be a problem. What if we go to /logout.php? Should we be able to do that?
Unvalidated redirections and forwards are hard to measure in terms of impact. They are probably more likely to be used as a vector for something else, whether it be phishing or stealing traffic.