Security Tip: Avoid Open Redirects!

[Tip#16] Ever clicked a link that looked legitimate, but took you somewhere unexpected?

Security Tip: Avoid Open Redirects!

👉 Looking to dive deeper into Laravel security? Check out Practical Laravel Security, my hands-on security course that uses interactive hacking challenges to teach you about how vulnerabilities work, so you can avoid them in your own code! 🕵️

Worried about your app being hacked? Book in a Laravel Security Audit and Penetration Test! I can find the vulnerabilities before a hacker does, and help you fix them! 🕵️


An Open Redirect vulnerability is where an attacker can trick the server into redirecting the victim somewhere specific, usually somewhere completely unrelated to the site the vulnerability is on. They are used to mask the final destination of a URL, so if the victim looks at the malicious open redirect link, all they see is a safe domain name at the beginning, not the real domain hiding at the end.

For example, consider an app that redirects a user to a login form with the intended authenticated URL in as a query string parameter.

A legitimate redirect could look like this:

https://example.com/login?redirect=/project/1

The user is shown the login form, and upon successful authentication, they are redirected to the “/project/1” route.

But, if the redirect is “open”, an attacker could send a victim to this URL:

https://example.com/login?redirect=https://evilsite.com

The user would see the valid login form, but then be redirected to a malicious page that could be absolutely anything. Yes, it wouldn’t have cookies, session, or login details, but you can get quite creative with it1.

What if the redirect existed on a URL that wasn’t a form endpoint, but rather responded to GET requests to trigger the redirect?

Something like this:

GET https://example.com/redirect?url=https://evilsite.com

If you throw that in an email or on a website, and pad it out a bit, it’s easy for an attacker to show the victim only this bit before they click on it:

https://example.com/redirect?url=...

As far as the victim is concerned, they are going to your safe website at example.com. But it will immediately redirect to the attacker’s evilsite.com. That’s your site reputation they are using to trick their victims, and the victim may not trust your site again.

How do we mitigate this?

  1. Inject the redirect URL into the session. This only works in some instances (like auth redirects), but when it’s possible, it is definitely the best solution as the attacker cannot modify the redirect destination.
    This is how Laravel does it by default with it’s authentication redirects.
  2. Use an allow-list of domains and/or URLs. Only complete the redirect request if the domain and/or URL being redirected to is on the allow-list. This gives you the freedom to redirect to multiple locations in different scenarios, without opening the gates to redirect anywhere.
  3. Hard-code the list of redirections in code/configuration. Very similar to #2, but rather than allow a dynamic redirection through an allow-list of domains/URLs, have all redirections listed and reference them by an identifier.

redirect()->back()

While we’re talking about this subject, I want to very briefly mention `redirect()->back()`, around which there is some discussion about open redirect vulnerabilities. `redirect()->back()` uses the Referrer header to know where to redirect the user back to, which means it’s technically an Open Redirect vulnerability (as the referrer header could be anything).

It is important to note however that in order to abuse this vulnerability, you need to control the Referrer headers in the browser. This cannot be done without having a level of control in the browser or over the request that makes an open redirect vulnerability meaningless. If you can modify the request to change headers, you can modify it to do anything else you like too.

So in my opinion, redirect()->back() is safe to use.


  1. Maybe the page shown after the login form could ask the user to reset their password, asking for email and password again to confirm as part of the flow. It’s a common pattern that gets used in apps!