2

Using PHP to set a Location header to redirect (e.g., upon login after being signed out) can be insecure if the redirect does not specify a domain. Here is an example:

header('Location: ' . $_GET['redirect']);

Is there a simple way (not a whitelist) to ensure that the redirect not leave the domain. Maybe something like:

header('Location: /' . $_GET['redirect']); //start forward slash guarantees domain cant change?
1

Maybe something like:

header('Location: /' . $_GET['redirect']); //start forward slash guarantees domain cant change?

No, a leading forward slash does not protect you against open redirects.

An attacker could query the script as...

https://yoursite.example/file.php?redirect=/malicious.example

...resulting in Location: //malicious.example which is treated as a protocol-relative URL that your browser will understand as a redirect to https://malicious.example.

This notation also works for URLs in other contexts, e.g. HTML links:

`<a href="//malicious.example">...</a>`.

Is there a simple way (not a whitelist) to ensure that the redirect not leave the domain.

An alternative simple approach to @David's answer is checking for a slash and a subsequent alphanumeric character, or the beginning sequence /./. Both can never lead to the representation of an absolute URL.

Note that in older versions of PHP you also had to sanitize a user-supplied header value for newlines/carriage returns as you might otherwise enable header injection attacks.


From your follow-up comment:

what if it started with a protocol and hostname? eg, header('Location: https://domain.com' . $_GET['redirect']) is there a way around that?

This is only safe if you enforce that the first user-supplied character is a slash. Otherwise, a simple attack would be .malicious.example, redirecting to the attacker-controlled subdomain https://domain.com.malicious.example. Alternatively, @malicious.example works too, since that turns domain.com into the username for authenticating with the domain malicious.example.

| improve this answer | |
  • that is good to know about the forward slash and alphanumeric character. what if it started with a protocol and hostname? eg, header('Location: https://domain.com' . $_GET['redirect'] is there a way around that? im just curious; thanks! – tau Jan 25 '18 at 0:36
  • 1
    @tau I expanded my answer on your example. – Arminius Jan 25 '18 at 4:32
2

Your example is trivially bypassable:

http://yoursite.com/page.php?redirect=/www.google.com

results in a redirect of:

//www.google.com

Which is a valid URL.

You should parse the URL using a function like parse_url then reconstruct a sanitized URL with the path components you want. For example, you could use just the path and query fragments to build a URL that redirects only within the current protocol, port, and host.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.