Open Redirect
A complete guide to understanding, detecting, exploiting, and preventing Open Redirect vulnerabilities.
Introduction
Open Redirect
The Complete ANAS EDUCATION Course (Beginner Edition)
1. Introduction
Imagine you open your PC and visit `anastech.com`.
You click "Login". The browser opens:
You type your email and password. You click "Submit". After a second, the page changes. The URL bar now shows:
You are logged in. You see your dashboard.
What just happened behind the scenes? Walk through it slowly.
- ●Your browser sent the username and password to the server.
- ●The server checked your credentials. They matched.
- ●The server replied with an HTTP response that contains a special header: `Location: /dashboard`.
- ●The browser saw that header and automatically opened the new URL.
That action ==> "the server tells the browser where to go next" ==> is called a redirect.
Now, here is something many login pages also do. They want to be nice to users who tried to visit a page before logging in. For example, if you tried to open `/transfers` but were not logged in, the site bounces you to `/login?next=/transfers`. After login, it sends you to `/transfers` instead of `/dashboard`.
The URL in your browser becomes:
The server reads that `next=/transfers` and uses it to know where to send you. Convenient.
But what if that `next` parameter is not just `/transfers`? What if someone changes it to:
If the server does not check, it will happily redirect you to `attacker.com` right after login. You will see the address bar change. The padlock disappears. But many users will not notice. The `attacker.com` page can be designed to look exactly like the login page, telling you "session expired, please log in again". You type your password. It is sent to the attacker.
That is the Open Redirect vulnerability. It is what happens when a feature that was supposed to take you to a safe page on the same site takes you wherever the URL says.
This course teaches that idea slowly and completely. By the end you will know:
- ●How a normal redirect works step by step.
- ●Where the danger lives.
- ●How attackers find and use the bug.
- ●Why it is more dangerous than it sounds, especially with OAuth.
- ●How developers should block it.
You do not need to be an expert. You just need to read carefully.
2. How It Works
To find this bug, you first need to understand redirects in detail.
Step 1. What a redirect actually is
A redirect is an HTTP response that tells the browser "this is not the answer; go look at a different URL". The most common form is:
When the browser sees this:
- ●Status code starts with `3` (301, 302, 303, 307, 308 are all redirects).
- ●The `Location` header gives the new URL.
- ●The browser makes a new request to that URL.
- ●The address bar updates to the new URL.
That is all. There is no warning. No popup. No "are you sure?". The browser just goes.
Step 2. Three ways a server can redirect you
A web app can perform a redirect in three different ways. All three behave the same from the user's perspective.
Way 1: HTTP `Location:` header.
The browser follows automatically.
Way 2: HTML meta refresh.
After 0 seconds, the browser opens the URL in the `url=` part.
Way 3: JavaScript navigation.
The browser runs the JavaScript, which sets the location.
All three are valid redirects. All three can be abused if the destination is attacker-controlled.
Step 3. Where the destination comes from
There are two big possibilities:
- ●The destination is hard-coded by the developer. Safe.
- ●The destination is taken from user input (URL parameter, header, body, cookie). Possibly dangerous.
Look at the code on the server:
The third version is the canonical open redirect.
Step 4. Walking through a vulnerable redirect
Imagine the URL the user clicks is:
Here is what happens, step by step:
- ●The browser sends `GET /login?next=https://attacker.com HTTP/1.1` to anastech.com.
- ●The login page renders. The user types email and password.
- ●The browser sends `POST /login HTTP/1.1` with the credentials.
- ●The server validates. The credentials are correct.
- ●The server reads the `next` parameter from the previous URL. The value is `https://attacker.com`.
- ●The server has no validation. It writes the value into the response:
- ●The browser sees the `Location` header. It opens `https://attacker.com`.
- ●The user is now on the attacker's site, but believes they are still on anastech.com because they just logged in successfully.
The attacker's site shows a fake "session expired" page. The user re-types their password. The attacker logs it.
Step 5. Why it is worse than it looks
A normal Open Redirect on its own is "just" a phishing tool. The user types their password on the attacker's page. That is bad, but limited.
The big damage starts when the redirect lives inside an OAuth or SAML flow. In those flows, the redirect carries a token, an authorization code, or both. If the redirect can be steered to an attacker server, the token is leaked. With the token, the attacker logs in as the victim. This is full account takeover.
That escalation is covered in detail in Section 11 (Exploitation, technique 13).
3. Attack Flow
The attacker follows a careful sequence. Read each step in order.
Step 1. Find every URL that takes a redirect-shaped parameter
Walk the site. Look at every URL the browser visits, especially after clicking these buttons:
For each one, look at the URL parameters. Watch for names like:
These are the names that historically hold the redirect destination.
Step 2. Send a clean external URL
For each candidate, change the parameter value to:
Send the request. Watch the response. There are three outcomes:
- ●Server replies with `Location: https://attacker.com/` ==> OPEN REDIRECT CONFIRMED.
- ●Server replies with `400 Bad Request` or "external URLs not allowed" ==> there is a filter. Go to Step 3.
- ●Server ignores the parameter and redirects to `/dashboard` ==> the parameter is not the redirect controller. Move on.
Step 3. If filtered, try the bypass ladder
The most common filters and their bypasses, in order:
Step 4. Confirm in a real browser
Some redirects only fire on specific status codes (303 vs 302), specific methods (POST vs GET), or with specific cookies. Use a real browser to be sure the redirect navigates the user. Open the URL. Watch the address bar change.
Step 5. Chain it (if possible)
A plain Open Redirect is medium-severity at best. The big payouts come from chains:
- ●OAuth chain. Bend `redirect_uri` to leak the authorization code. Becomes account takeover.
- ●SAML chain. Bend `RelayState` to leak the SAML assertion.
- ●XSS chain. Use `javascript:` URL to execute script in the target origin.
- ●SSRF chain. When a server-side fetcher follows redirects, point it at internal addresses.
- ●Token theft via Referer. When a password-reset link redirects to attacker, the Referer header leaks the reset token.
Step 6. Document and report
Each open-redirect engagement follows this exact heartbeat.
4. Why Developers Make This Mistake
The developer who wrote this:
was thinking:
- ●"After login, the user should land on the page they tried to reach."
- ●"I read the `next` parameter from the URL. That is where they wanted to go."
- ●"Redirects are safe. The browser just goes to the URL."
Each of those three thoughts has a hidden flaw. The reasons:
- ●"The user wanted to go there." The user did not type the parameter. The link did. If the link came from a phishing email, the destination is the attacker's choice, not the user's.
- ●"I read the URL parameter." A URL parameter is user-controlled input. It must be validated. Reading it does not validate it.
- ●"Redirects are safe." Redirects are mechanically safe (the browser just changes location). The danger is contextual: the new URL might be a fake login page, an OAuth code leaker, or a `javascript:` URL.
The deeper reason behind the mistake: developers think about the feature (user returns to the page they came from), attackers think about the side effect (this is a free phishing primitive on our trusted domain).
Other common misconceptions:
- ●"Browsers warn users when the domain changes." False. They do not.
- ●"Only URLs starting with / are local." False. `//attacker.com` is protocol-relative and goes to attacker.com.
- ●"I check that the URL starts with /." Insufficient. `/\attacker.com` decodes after the check.
- ●"I check that the URL contains anastech.com." Insufficient. `https://anastech.com.attacker.com` contains it but is not it.
- ●"Browser auto-fill will warn." False. Users type passwords on phishing pages every day.
It is not a single coding error. It is a mental model bug about whose decision the destination really is.
5. Beginner Summary
- ●An open redirect happens when a website sends the user to a URL chosen by an attacker, because the destination came from a URL parameter and was not checked.
- ●The simplest test is `?next=https://attacker.com` ==> if the server actually goes to attacker.com, the bug is there.
- ●The damage is usually phishing, but in OAuth, SAML, and SSO flows it becomes full account takeover.
- ●Developers usually try to block external URLs but miss bypasses like `//attacker.com`, `https://anastech.com@attacker.com`, `https://anastech.com.attacker.com`, or `javascript:` URLs.
- ●The fix: hard-code internal destinations, use an allowlist of allowed keys, validate the host (not the substring), and reject every non-http(s) scheme.
If you remember those five lines, you have the whole concept.
6. Visual Explanation
The safe pattern
The vulnerable pattern
The OAuth chain pattern
The bypass ladder
Read those four diagrams. They are the whole bug class.
7. Definition
Technical definition. Open Redirect is a vulnerability in which a web application performs a redirect (HTTP `Location`, meta refresh, or JavaScript navigation) to a destination derived from user-controlled input without sufficient validation, allowing an attacker to craft URLs that redirect victims to arbitrary domains. When chained with authentication flows it frequently escalates to authorization code theft and account takeover. Tracked under CWE-601 (URL Redirection to Untrusted Site).
Beginner-friendly definition. An open redirect is when a website sends you to a link of someone else's choice, just because someone put it in a URL parameter.
Why it matters. Open Redirect is the swiss-army primitive of modern web hacking. On its own it powers phishing campaigns that bypass mail filters because the link starts with a trusted domain. Chained with OAuth or SAML it becomes account takeover (CVE-2024-52289 Authentik, CVE-2023-6927 Keycloak). Chained with reflected XSS it becomes stored or persistent XSS. Chained with SSRF it becomes internal network reconnaissance. Recent critical incidents include CVE-2025-4123 (Grafana open redirect chained to stored XSS and SSRF), CVE-2025-69725 (go-chi RedirectSlashes), CVE-2024-52289 (Authentik), CVE-2023-22797 (Rails Action Pack), and CVE-2023-6927 (Keycloak).
Common affected systems.
- ●Login flows that remember "where you came from"
- ●Logout flows that take a `?return=` URL
- ●Password reset emails with a callback URL
- ●Email confirmation links with a destination parameter
- ●OAuth authorize and callback endpoints (`redirect_uri`)
- ●SAML AssertionConsumerServiceURL and RelayState
- ●Single Sign-On (SSO) idP-initiated flows
- ●Checkout pages with `success_url` and `cancel_url`
- ●Marketing tracking redirects (`/r/?url=...`, `/go?to=...`)
- ●CMS short-link / vanity-URL handlers
- ●Mobile-app deep links and universal links
- ●Email click trackers
If a feature lets a user provide a URL that becomes part of a redirect decision, Open Redirect may live there.
8. Examples
Five realistic scenarios. Each is a small walkthrough, no characters.
Example 1. Plain Login Return URL
The feature. A bank's login page accepts a `?next=` parameter and redirects to it after authentication.
The bug. No validation on `next`. Any URL works.
The attack step by step.
- ●The attacker crafts a phishing email containing:
- ●Mail filters trust the link because it starts with the legitimate bank.
- ●The user clicks. The browser opens the real bank login page.
- ●The user types real credentials. Login succeeds.
- ●The bank reads `next` and sends a `Location` header to the attacker.
- ●The browser opens the attacker's page, which looks like a "confirm your card details" follow-up.
- ●The user fills in PAN, CVV, PIN. The attacker logs them.
Example 2. Logout Redirect
The feature. A social app lets third-party integrations log users out and send them to a follow-up page.
The bug. `returnTo` is the redirect with no validation.
The attack step by step.
- ●The attacker posts a link in a public forum:
- ●Any user who clicks gets logged out (slightly annoying) and bounced to a clone page that says "Your session expired, please log in again".
- ●The clone steals credentials.
Example 3. OAuth `redirect_uri` Bypass
The feature. An identity provider validates OAuth `redirect_uri` by checking that the URL "starts with" the registered callback.
The bug. `startswith` is a substring check. Registered: `https://client.com/cb`. Attacker submits: `https://client.com/cb.attacker.com/`. Check passes (the string really does start that way), but the host is `attacker.com`.
The attack step by step.
- ●The attacker crafts an authorize URL using the legitimate client's `client_id` and their own redirect target:
- ●A victim logs in.
- ●The authorization code lands on `client.com/cb.attacker.com` (an attacker server).
- ●The attacker exchanges the code for an access token.
- ●Account takeover.
This is the CVE-2024-52289 (Authentik) pattern.
Example 4. SSRF + Open Redirect Chain
The feature. A document app has an "import from URL" feature that fetches a URL server-side. It has SSRF protection: a regex disallowing internal IPs. It also has a public redirect endpoint at:
The bug. The SSRF protection checks the host of the URL it is about to fetch. But the fetcher follows redirects. The public redirect endpoint is open.
The attack step by step.
- ●The attacker imports the URL:
- ●The SSRF fetcher checks the host: `anasdocs.com`. Check passes.
- ●The fetcher requests it.
- ●The server responds with `Location: http://169.254.169.254/latest/meta-data/`.
- ●The fetcher follows.
- ●AWS metadata returns. IAM credentials leak.
Example 5. javascript: URI Chain (Grafana-style)
The feature. A monitoring app uses a public redirect endpoint that decodes a path parameter:
The bug. The decoder accepts `javascript:` URLs.
The attack step by step.
- ●The attacker encodes a payload:
- ●Base64-encodes and sends:
- ●A logged-in user clicks. The redirect uses `window.location =` with the decoded value.
- ●Since the page is `https://anastwo.com/`, the JavaScript executes in that origin.
- ●Cookie theft. Account takeover.
This mirrors the CVE-2025-4123 Grafana chain.
Each pattern shows up in real disclosed reports. The mechanics never change.
9. Vulnerable Code
Python (Flask) ==> Critical Open Redirect
What is wrong: `redirect()` accepts an absolute URL. Flask does not validate it. The browser obeys.
Python (Django) ==> Limited but still possible
Django's `redirect()` calls `HttpResponseRedirect` and accepts external URLs. Use `url_has_allowed_host_and_scheme()` to validate.
Python (FastAPI) ==> Open Redirect
PHP ==> Critical Open Redirect
`header("Location: ...")` accepts any URL. Bonus: if `$next` contains `\r\n`, this is also HTTP Response Splitting (CRLF injection).
PHP ==> Insufficient validation
`strpos($next, 'anasbank.com') === false` returns false for `https://anasbank.com.attacker.com` (contains the substring but is not the host). Substring check, not host check.
Node.js (Express) ==> Critical Open Redirect
Node.js (Express) ==> Weak validation
`next.startsWith('/')` blocks absolute URLs but allows `//attacker.com` (protocol-relative) and `/\attacker.com` (backslash trick, browsers normalize to `//attacker.com`).
Java (Spring Boot) ==> Critical Open Redirect
Spring's `redirect:` prefix takes any URL. No validation.
Java (Servlet) ==> Critical Open Redirect
Ruby (Rails) ==> Pre-7.0 Open Redirect
Rails 7.0 introduced `allow_other_host: false` as default. Before 7.0, this was a textbook open redirect. CVE-2023-22797 showed even the new check could be bypassed by carefully crafted URLs.
Ruby (Sinatra) ==> Critical Open Redirect
.NET (ASP.NET Core MVC) ==> Open Redirect
ASP.NET provides `LocalRedirect()` which throws if the URL is not local. Use that.
Go (net/http) ==> Open Redirect
Go (go-chi) ==> CVE-2025-69725
`RedirectSlashes` in versions >= 5.2.2 contained URL normalization that allowed attackers to manipulate the redirect target.
JavaScript (client-side) ==> DOM-based Open Redirect
Anything in `?next=` becomes the destination, including `javascript:` URLs which yield XSS.
Meta Refresh Open Redirect
Rendered with `next` taken from the query string. Same vulnerability, different mechanism.
The universal pattern across languages
Step 1, step 2, and step 3 together are where the bug lives.
10. Detection
Detection is the step where you confirm a bug exists. Walk through each test in order.
Step 1. List every redirect-prone endpoint
Step 2. List candidate parameters
The top 50 from years of bug bounty hunting:
Step 3. Probe each candidate in three tiers
Step 4. Watch four signals on every probe
- ●HTTP `Location:` header in the response.
- ●HTTP status code (301, 302, 303, 307, 308).
- ●Response body if it contains a meta refresh.
- ●Response body if it contains a JavaScript navigation.
If the `Location:` header is your external URL, the redirect is open.
Step 5. Confirm in a real browser
Some redirects only fire under certain conditions. Open the URL in Chrome and Firefox. Watch the address bar.
Burp Suite step by step
- ●Use Burp Proxy to intercept the original redirect-using request.
- ●Send to Repeater. Change one redirect-looking parameter at a time.
- ●Use Intruder with the bypass payload list.
- ●Use the Reflected Parameters extension to find parameters reflected in `Location:`.
- ●Use ParamMiner to discover hidden parameters.
Automated tools
- ●OpenRedireX ==> https://github.com/devanshbatham/OpenRedireX ==> Async fuzzer specifically for open redirect.
- ●Oralyzer ==> https://github.com/r0075h3ll/Oralyzer ==> Open Redirect scanner with many bypass payloads.
- ●Nuclei templates ==> tagged `redirect` ==> hundreds of known open-redirect signatures.
- ●kxss by Tom Hudson ==> reflected-parameter discovery often surfaces redirect candidates.
- ●ffuf ==> with a parameter wordlist against suspect endpoints.
Quick command:
Indicators of vulnerability
- ●An app that "remembers where you came from" after login or logout.
- ●Any parameter named `next`, `redirect`, `url`, `return`, `returnTo`, `to`, `dest`, `continue`.
- ●An OAuth flow with `redirect_uri=` in the URL.
- ●A SAML flow with `RelayState=`.
- ●Marketing or email-tracking redirects (`/r?url=...`, `/click?u=...`).
- ●Mobile-app deep links that bounce through the web.
- ●Responses with a `Location:` header echoing query-string content.
- ●Pages with `<meta http-equiv="refresh">` or client-side JavaScript reading `?next=`.
A simple detection script
If the printed redirect URL is your `attacker.com`, you have a hit.
11. Exploitation
This is where detection becomes impact.
Workflow
Advanced techniques (numbered 1 to 30)
1. Protocol-relative URL
The browser inherits the current scheme (HTTPS) and goes to `attacker.com`. Filters that check `startsWith("/")` pass it because the first character is `/`.
2. Backslash trick
Filters that check `startsWith("/")` pass `/\`. Browsers normalize `\` to `/`, so `/\attacker.com` becomes `//attacker.com` and goes to `attacker.com`.
3. Userinfo (@) trick
To a substring check, the URL contains `anastech.com`. But in URL syntax, the part before `@` is the userinfo, and the host is everything after `@`. The browser goes to `attacker.com`.
4. Subdomain confusion
`anastech.com.attacker.com` is a subdomain of `attacker.com`. Substring checks miss this. Host-suffix checks miss it unless implemented as `endsWith(".anastech.com")` with a leading dot.
5. Fragment and query confusion
Substring checks see `anastech.com`. The browser ignores it (fragment is client-side only).
6. URL encoding bypass
If the filter checks the encoded string but the redirect decodes first, the bypass works.
7. Double URL encoding
8. Overlong UTF-8
Some legacy parsers accept overlong UTF-8 encodings of `/`.
9. IDN / Punycode homograph
Register `xn--anstch-...` (Cyrillic `а` instead of Latin `a`). Substring filters comparing to `anastech.com` (ASCII) miss it. Browsers render the homograph. Semrush paid a bounty for exactly this pattern.
10. javascript: URI
For client-side and meta-refresh redirects:
The browser evaluates `javascript:` URLs in the current origin. Open Redirect becomes XSS.
11. data: URI
Same idea, different scheme. Modern browsers limit `data:` URI top-level navigation, but it still works in some contexts.
12. CRLF injection in Location
If the framework concatenates input into the `Location:` header without sanitizing CR/LF, the attacker can inject additional headers.
13. OAuth redirect_uri bypass chain
The crown jewel. If the OAuth provider validates with `startsWith` or regex with unescaped dots:
If the registered URL has wildcards (Keycloak supports `*`):
CVE-2023-6927 (Keycloak), CVE-2024-52289 (Authentik) are real cases.
14. SAML RelayState abuse
After a successful SAML SSO, the SP redirects to the value of RelayState. Many SPs do not validate it.
15. Host header injection to Open Redirect
Frameworks that derive redirect URLs from the `Host` header:
The response contains:
16. Referer leak chain for token theft
When the redirect goes to attacker.com with a sensitive token in the URL:
- ●Authorization code in URL after OAuth redirect.
- ●Password-reset token in URL on a reset flow.
- ●CSRF token in URL.
The attacker captures the Referer header from their server logs.
17. IP format tricks
18. Path traversal in redirect target
19. Open Redirect to SSRF bypass
If an SSRF fetcher checks host before fetch but follows redirects:
The fetcher checks `anasdocs.com` ==> passes. Follows the redirect. SSRF.
20. Open Redirect to XSS via javascript:
Covered in technique 10. The most common chain. Counts as critical because it gives the attacker JavaScript execution in the target origin.
21. Cache poisoning via redirect
If an intermediate cache stores the response of a redirect keyed by URL but the response was generated from an attacker-controlled header:
The cache stores a redirect to `attacker.com` for future users.
22. Mobile deep-link bridges
Mobile apps register custom URL schemes. A web-to-app bridge that follows `next=` can launch the app into an attacker-chosen action.
23. SSO logout-then-login race
Some SSO systems log the user out, then redirect to a `?next=` URL. The brief window where the user has no session but the redirect URL still carries their identifier can be raced.
24. Auth bypass via redirect to same app
When the redirect target is an internal URL that performs a state change, and the auth check happens BEFORE the redirect, the redirect re-runs the request with the now-authenticated session.
25. Multi-step redirects (reflected forward)
Some apps chain two redirects: a public `/r` endpoint that calls an internal `/forward` that itself does another redirect. Each hop is its own validation. The attacker exploits the weakest hop.
26. State parameter misuse for secondary redirect
In OAuth, the `state` parameter is for CSRF protection. Some apps reuse it as a secondary "where to go":
27. URL confusion via different parser behaviors
The browser, the proxy, the framework, the WAF, and the application can disagree on how to parse the same URL string:
If the WAF parses one way and the application parses another, only the application's view matters for the redirect.
28. Open Redirect via filename / suffix match
When the validator checks "does the URL end with `.anastech.com`?":
Endswith on the raw string passes. The host is `attacker.com`.
29. Open Redirect via Markdown / rich text renderers
When the application renders markdown that includes URLs:
If the renderer does not strip dangerous schemes, the resulting `<a href="javascript:...">` is XSS on click.
30. SSO IdP-initiated redirect
Some SSO providers allow IdP-initiated SSO with a `RelayState`. If the SP trusts RelayState and redirects post-login, the attacker constructs an IdP-initiated SSO URL with `RelayState=https://attacker.com/`.
These 30 techniques are the modern Open Redirect hunter's toolkit. Memorize. Combine. Chain.
12. Proof of Concept
Burp Suite step by step
curl PoC
Python PoC (detection)
Python PoC (OAuth chain)
Python PoC (attacker listener)
Deploy on `attacker.com`. Every visit logs the URL and Referer, which often contain leaked codes.
Bash PoC (quick scanner)
OpenRedireX PoC
Custom Nuclei template
PowerShell PoC
13. Payloads
Top 50 redirect parameters
Tier 1 ==> Naked external URLs
Tier 2 ==> Protocol-relative and slash tricks
Tier 3 ==> Userinfo and host tricks
Tier 4 ==> Scheme tricks
Tier 5 ==> URL encoding bypasses
Tier 6 ==> IDN, Punycode, homograph
Tier 7 ==> IP tricks
Tier 8 ==> CRLF injection in Location
Tier 9 ==> Combined tricks
Tier 10 ==> OAuth-specific payloads
Tier 11 ==> SAML specific payloads
Tier 12 ==> Host header injection
Generic master list
For automated fuzzing, compile this as `payloads.txt`:
14. Wordlists and Payload Libraries
- ●PayloadsAllTheThings ==> Open Redirect ==> https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Open%20Redirect
- ●OpenRedireX ==> https://github.com/devanshbatham/OpenRedireX
- ●Oralyzer ==> https://github.com/r0075h3ll/Oralyzer
- ●HackTricks ==> Open Redirect ==> https://book.hacktricks.xyz/pentesting-web/open-redirect
- ●HackTricks ==> OAuth to Account Takeover ==> https://book.hacktricks.xyz/pentesting-web/oauth-to-account-takeover
- ●PortSwigger ==> Open Redirect ==> https://portswigger.net/web-security/dom-based/open-redirection
- ●OWASP Unvalidated Redirects and Forwards Cheat Sheet ==> https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html
- ●SecLists ==> Fuzzing ==> Polyglots ==> https://github.com/danielmiessler/SecLists/tree/master/Fuzzing/Polyglots
- ●HackerOne TOPOPENREDIRECT.md ==> https://github.com/reddelexc/hackerone-reports/blob/master/tops_by_bug_type/TOPOPENREDIRECT.md
- ●HackerOne TOPOAUTH.md ==> https://github.com/reddelexc/hackerone-reports/blob/master/tops_by_bug_type/TOPOAUTH.md
- ●Top 25 Open Redirect Reports (Cristian Cornea) ==> https://corneacristian.medium.com/top-25-open-redirect-bug-bounty-reports-5ffe11788794
- ●Anas Magane Pentesting Notes ==> https://github.com/Anas-Magane/Pentesting/tree/main/TOP_10_OWASP/Open_Redirect
15. Impact
- ●Phishing host. The number-one impact. The attacker abuses the trusted domain to deliver phishing pages.
- ●Credential theft. Cloned login pages collect usernames and passwords.
- ●OAuth account takeover. Leaked authorization code becomes a full token. CVE-2024-52289 Authentik, CVE-2023-6927 Keycloak. Bounty: $3,000 to $50,000+.
- ●SAML account takeover. Leaked SAML assertion equals ATO.
- ●Token leak via Referer. Password-reset tokens leak when redirected to attacker domains.
- ●XSS chain. `javascript:` and `data:` URI redirects execute attacker JavaScript in the target origin.
- ●SSRF chain. Open Redirect on a server-side fetcher leads to internal network access, cloud metadata theft.
- ●Cache poisoning. Redirect built from a header an intermediate cache trusts pollutes the cache for all users.
- ●Session fixation. Some redirect flows set a session ID in a URL parameter.
- ●Mobile app deep-link abuse. Web-to-app bridges launch the app into attacker-chosen actions.
- ●Reputation damage and brand abuse. Security teams that find their brand in phishing lists do damage control.
- ●Defeating modern phishing detection. Many anti-phishing engines trust the first hop's domain.
A "plain" open redirect is medium-severity at most. A chained open redirect can sit at the top of any program's payout tier.
16. Prevention
Vulnerable example
Secure example (allowlist of paths)
Secure example (host-validated external URLs)
Framework-specific safe APIs
- ●Django: `django.utils.http.url_has_allowed_host_and_scheme(url, allowed_hosts)`
- ●ASP.NET Core: `Url.IsLocalUrl(url)` and `LocalRedirect(url)`
- ●Rails 7+: `redirect_to ..., allow_other_host: false`
- ●Flask: Use your own `is_safe_redirect` or `werkzeug.urls.url_parse` + host comparison.
- ●Express: Use the `url` module to parse and host-check.
- ●Spring: Validate URL via `UriComponentsBuilder` and check the host.
- ●Go: Use `url.Parse` and compare `u.Host` to an allowlist.
Eight Rules to Eliminate Open Redirect
- ●Rule 1. Prefer redirects to fixed, hard-coded paths.
- ●Rule 2. When user choice is needed, use an allowlist of destination keys, not URLs.
- ●Rule 3. When external URLs must be allowed, parse the URL, extract the host, and validate against an explicit allowlist.
- ●Rule 4. Reject `//`, `/\`, `\`, `javascript:`, `data:`, `vbscript:`, `file:`, and any other scheme except `http`/`https`.
- ●Rule 5. Validate before AND after any decoding.
- ●Rule 6. Use the framework's safe API.
- ●Rule 7. For OAuth providers: exact-match `redirect_uri`. No wildcards. No `startsWith`. No regex with unescaped dots.
- ●Rule 8. Add an interstitial page for any external redirect: "You are leaving anastech.com..."
Developer checklist
Server/framework configuration examples
Django:
ASP.NET Core:
Rails:
Express:
17. Real-World Cases
CVE-2025-4123 (Grafana ==> Open Redirect ==> Stored XSS ==> SSRF)
Grafana OSS and Enterprise had an open redirect via path traversal in the public redirect handler. Chained with the application's XSS sink, attackers achieved stored XSS, which then enabled SSRF (full read of internal endpoints) against the Grafana process.
CVE-2025-69725 (go-chi RedirectSlashes)
The go-chi router's `RedirectSlashes` middleware in versions >= 5.2.2 contained URL normalization that allowed remote attackers to redirect victim users to malicious websites while keeping the legitimate domain in the URL.
CVE-2024-52289 (Authentik OAuth ATO via Regex-Unescaped Dots)
Authentik validated OAuth `redirect_uri` using regex matching. The registered URIs were not escaped properly, so the period character (`.`) was interpreted as the "any character" wildcard. An attacker who could observe a registered redirect URI of `https://client.com/cb` could supply variations and pass the regex check. Result: one-click OAuth account takeover.
CVE-2023-22797 (Rails Action Pack Open Redirect)
Rails 7.0 introduced `allow_other_host: false` as the default for `redirect_to`. The check had a bypass: carefully crafted URLs could fool the validation. Fixed in 7.0.4.1.
CVE-2023-6927 (Keycloak Open Redirect via Wildcard redirect_uri)
Keycloak supports wildcard `*` in registered redirect URIs. When a wildcard is used poorly (e.g. `https://*.client.com/*`), an attacker can craft a URI that matches the wildcard but points to attacker infrastructure.
HackerOne #3099816 ==> Lichess Open Redirect in OAuth Flow
The OAuth flow on Lichess accepted any `redirect_uri` without strict validation. Changing the `redirect_uri` from the legitimate URL to `https://example.com/` succeeded.
HackerOne #76738 ==> Zaption Triple-Slash Filter Bypass
Triple slash bypass.
HackerOne #1444675 ==> Omise Host Header Injection
The application built redirect URLs from the `Host` header.
HackerOne #2828499 ==> Localize Host-Header Open Redirect
Same pattern as Omise.
HackerOne ==> Shopify checkout_url Open Redirect
Shopify's checkout URL accepted attacker-controlled redirect-style values.
HackerOne ==> Starbucks Open Redirect + Reflected XSS Chain
Starbucks had an open redirect that, combined with a reflected XSS payload, hit all their `*.starbucks.com` storefronts.
HackerOne ==> Semrush IDN Homograph OAuth ATO
A hunter registered a homograph domain and used it as `redirect_uri`. The OAuth flow completed, the access token landed on the homograph. 260 upvotes.
HackerOne ==> X / xAI Periscope OAuth Callback ATO
Insufficient OAuth callback validation enabled Periscope account takeover. 273 upvotes.
HackerOne ==> GitLab Email Verification Bypass for OAuth ATO
$3,000 bounty. 254 upvotes.
HackerOne ==> pixiv Stealing OAuth Authorization Code
$2,000 bounty. 244 upvotes.
HackerOne ==> Mail.ru Open Redirect on My.com
Plain open redirect.
HackerOne ==> GSA Bounty idp.fr.cloud.gov Open Redirect
A US government identity provider had an open redirect. $150 bounty.
Lessons across all these cases:
- ●Plain open redirects pay little. Chained open redirects pay a lot.
- ●OAuth + open redirect = account takeover, almost without exception.
- ●Regex-based redirect_uri validation is a recurring source of CVEs. Exact-match is the only safe option.
- ●Wildcards in redirect_uri lead to bypasses.
- ●Even hardened frameworks (Rails 7) ship bypasses.
- ●Host-header injection still works in 2024-2026 on many stacks.
- ●The number-one finding source is the `returnTo`, `next`, and `redirect_uri` parameters.
18. References
- ●OWASP Unvalidated Redirects and Forwards Cheat Sheet ==> https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html
- ●MITRE CWE-601 ==> https://cwe.mitre.org/data/definitions/601.html
- ●PortSwigger ==> DOM-based Open Redirection ==> https://portswigger.net/web-security/dom-based/open-redirection
- ●PortSwigger ==> OAuth Authentication ==> https://portswigger.net/web-security/oauth
- ●HackTricks ==> Open Redirect ==> https://book.hacktricks.xyz/pentesting-web/open-redirect
- ●HackTricks ==> OAuth to Account Takeover ==> https://book.hacktricks.xyz/pentesting-web/oauth-to-account-takeover
- ●PayloadsAllTheThings ==> Open Redirect ==> https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Open%20Redirect
- ●OpenRedireX ==> https://github.com/devanshbatham/OpenRedireX
- ●Oralyzer ==> https://github.com/r0075h3ll/Oralyzer
- ●HackerOne TOPOPENREDIRECT.md ==> https://github.com/reddelexc/hackerone-reports/blob/master/tops_by_bug_type/TOPOPENREDIRECT.md
- ●HackerOne TOPOAUTH.md ==> https://github.com/reddelexc/hackerone-reports/blob/master/tops_by_bug_type/TOPOAUTH.md
- ●Top 25 Open Redirect Reports ==> https://corneacristian.medium.com/top-25-open-redirect-bug-bounty-reports-5ffe11788794
- ●CVE-2025-4123 Grafana ==> https://hackerone.com/reports/3286945
- ●CVE-2025-69725 go-chi ==> https://www.sentinelone.com/vulnerability-database/cve-2025-69725/
- ●CVE-2024-52289 Authentik writeup ==> https://securityblog.omegapoint.se/en/writeup-authentik-cve-2024-52289/
- ●CVE-2023-6927 Keycloak writeup ==> https://securityblog.omegapoint.se/en/writeup-keycloak-cve-2023-6927/
- ●CVE-2023-22797 Rails ==> https://hackerone.com/reports/1865991
- ●Lichess OAuth #3099816 ==> https://hackerone.com/reports/3099816
- ●Zaption #76738 ==> https://hackerone.com/reports/76738
- ●Omise #1444675 ==> https://hackerone.com/reports/1444675
- ●Localize #2828499 ==> https://hackerone.com/reports/2828499
- ●Anas Magane Pentesting Notes ==> https://github.com/Anas-Magane/Pentesting/tree/main/TOP_10_OWASP/Open_Redirect
19. Practical Labs
SOON.
The ANAS EDUCATION lab environment for Open Redirect is currently being built. You will soon practice:
- ●Naked redirect (no filter)
- ●Filter that requires `/` prefix (bypass with `//` and `/\`)
- ●Filter that requires "contains anastech.com" (bypass with `@` and `.attacker.com`)
- ●URL-encoded and double-encoded bypasses
- ●CRLF injection in `Location:` header
- ●OAuth `redirect_uri` exact-match bypass via path traversal
- ●OAuth `redirect_uri` regex bypass with unescaped dots (Authentik-style)
- ●OAuth `redirect_uri` wildcard bypass (Keycloak-style)
- ●SAML RelayState abuse
- ●Host header injection leading to open redirect
- ●Open redirect chained with reflected XSS via `javascript:`
- ●Open redirect chained with SSRF via internal fetcher follow-redirects
- ●Token leak via Referer header on a password-reset flow
- ●Mobile deep-link bridge abuse
- ●Cache poisoning via `X-Forwarded-Host`
- ●IDN/Punycode homograph bypass
In the meantime, practice on PortSwigger Web Security Academy labs:
- ●APPRENTICE: OAuth account hijacking via redirect_uri
- ●PRACTITIONER: Stealing OAuth access tokens via an open redirect
- ●PRACTITIONER: SSRF via OAuth flow
- ●PRACTITIONER: Forced OAuth profile linking
- ●EXPERT: Authentication bypass via OAuth implicit flow
- ●EXPERT: Stealing OAuth access tokens via a proxy page
Stay tuned.
20. Cheat Sheet
21. Exam (30 Questions)
Format: Multiple Choice. Platform randomly selects 20. Scoring: 0 to 13 fail, 14 to 15 retry, 16 to 20 pass.
Q1. Which CWE matches Open Redirect most directly? A. CWE-79 B. CWE-89 C. CWE-601 D. CWE-22 Answer: C.
Q2. A protocol-relative URL like `//attacker.com` is dangerous because: A. It is invalid B. The browser inherits the current scheme and goes to attacker.com C. It always points to localhost D. It blocks redirects Answer: B.
Q3. The `@` symbol in `https://anastech.com@attacker.com`: A. Is the host B. Marks the start of the host C. Splits userinfo from host; the host is everything after the @ D. Is ignored by browsers Answer: C.
Q4. Which parameter is NOT a typical redirect parameter name? A. next B. returnTo C. csrf_token D. redirect_uri Answer: C.
Q5. An open redirect chained with OAuth typically results in: A. Phishing only B. Authorization code theft and account takeover C. SQL injection D. Stored XSS Answer: B.
Q6. Which Rails 7+ option prevents redirect_to from leaving the host? A. `allow_redirects: false` B. `allow_other_host: false` C. `allow_external: false` D. `safe_redirect: true` Answer: B.
Q7. Which CVE is the Authentik OAuth regex-unescaped-dot bypass? A. CVE-2025-4123 B. CVE-2023-6927 C. CVE-2024-52289 D. CVE-2025-69725 Answer: C.
Q8. The Grafana 2025 chain combined: A. SQL injection + RCE B. Open Redirect + Stored XSS + SSRF C. CSRF + XSS only D. Path traversal + LFI Answer: B.
Q9. A `redirect_uri` registered as `https://client.com/cb` is bypassed by: A. `https://client.com/cb` B. `https://client.com/cb.attacker.com/` C. `https://attacker.com` D. `/cb` Answer: B.
Q10. Which is NOT a safe redirect API? A. `LocalRedirect` in ASP.NET B. `url_has_allowed_host_and_scheme` in Django C. `header("Location: " . $_GET['next'])` in PHP D. `redirect_to ..., allow_other_host: false` in Rails 7 Answer: C.
Q11. `javascript:alert(1)` as a redirect target results in: A. A 404 B. XSS in the current origin C. Nothing D. SSRF Answer: B.
Q12. The `RelayState` parameter in SAML can be: A. A signed timestamp B. An attacker-controlled redirect target if not validated C. The SAML signing key D. The user's email Answer: B.
Q13. Host header injection becomes Open Redirect when: A. The Host header is encrypted B. The application builds redirect URLs from the `Host` header C. The Host header equals the user's IP D. Never Answer: B.
Q14. The bypass `/\attacker.com` works because: A. The browser ignores `/\` B. Filters that require a `/` prefix accept `/\`, and the browser normalizes `\` to `/` C. It is rejected by all browsers D. It is HTTPS-only Answer: B.
Q15. PKCE in OAuth helps because: A. It encrypts the redirect_uri B. It binds the authorization code to the requester, making leaked codes useless C. It hides the client_id D. It signs the access token Answer: B.
Q16. A substring check `if (url.contains("anastech.com"))` is bypassed by: A. `https://anastech.com.attacker.com/` B. `https://attacker.com` C. `/login` D. `https://anastech.com/` Answer: A.
Q17. IDN homograph attacks abuse: A. CSS rendering B. Unicode characters that look like ASCII letters but resolve to a different domain C. HTML parsing D. Browser cache Answer: B.
Q18. Which response status is NOT a redirect? A. 301 B. 302 C. 307 D. 404 Answer: D.
Q19. The safest design for "remember where you came from" after login is: A. Take any URL from a parameter B. Map a key like `?next=profile` to a server-side allowlist C. Use the Referer header D. Store the URL in a cookie Answer: B.
Q20. CVE-2023-6927 (Keycloak) was about: A. SQL injection B. Wildcard redirect_uri matching that allowed attacker subdomains to pass the check C. CSRF D. Path traversal Answer: B.
Q21. Which Open Redirect bypass uses CRLF? A. `?next=/foo%0d%0aLocation:%20https://attacker.com` B. `?next=javascript:alert(1)` C. `?next=//attacker.com` D. `?next=/` Answer: A.
Q22. An open redirect on a password-reset link leaks tokens via: A. The Set-Cookie header B. The Referer header (or the URL if the redirect carries the token) C. The Server header D. The Content-Type header Answer: B.
Q23. ASP.NET's `Url.IsLocalUrl(url)` returns true for: A. `https://attacker.com` B. `//attacker.com` C. `/dashboard` D. `javascript:alert(1)` Answer: C.
Q24. In which case does an Open Redirect become a critical issue? A. When it is on a static blog B. When it lives inside an OAuth or SAML flow with token transit C. When it returns 200 OK D. When the user is logged out Answer: B.
Q25. A `data:` URI in a client-side redirect: A. Always 404s B. Can execute JavaScript in the current origin (with limits in modern browsers) C. Always loads from the network D. Is unrelated to redirects Answer: B.
Q26. The "interstitial page" mitigation works by: A. Encrypting the redirect B. Showing "You are leaving anastech.com..." which adds friction and visibility C. Logging the user out D. Blocking the redirect entirely Answer: B.
Q27. Wildcard `redirect_uri` in OAuth: A. Is recommended B. Should be avoided; it routinely leads to ATO via attacker-controlled subdomains C. Improves security D. Is required by the OAuth spec Answer: B.
Q28. Cache poisoning via Open Redirect happens when: A. The cache is empty B. The cache stores the redirect response keyed by URL but generated from an attacker-controlled header C. The user logs in D. HTTPS is disabled Answer: B.
Q29. The most reliable Open Redirect bypass to try first is: A. CSRF token forgery B. SQL injection C. `//attacker.com` (protocol-relative) D. Path traversal Answer: C.
Q30. The MOST important takeaway about Open Redirect: A. It is harmless B. It is a primitive that scales with context; in OAuth or SAML it is account takeover C. It only matters on login pages D. Modern browsers prevent it Answer: B.
22. Certificate Requirements
To earn the ANAS EDUCATION Open Redirect Certificate, the student must:
- ●Complete every lesson in this module.
- ●Complete all practical labs once released.
- ●Pass the exam with at least 16 out of 20.
Only then will the course be marked complete on the student dashboard.
23. Important Notes
Common Beginner Mistakes
- ●Reporting "Open Redirect to evil.com" without exploring chains.
- ●Forgetting `//attacker.com` is protocol-relative.
- ●Testing only `?next=` and missing `returnTo`, `redirect_uri`, `r`, `u`, `to`.
- ●Not testing OAuth and SAML flows.
- ●Not testing meta-refresh and client-side JavaScript redirects.
- ●Confusing "contains the domain" with "is the domain".
- ●Giving up at the first 400/403.
- ●Missing `javascript:` and `data:` chains.
Pentester Tips
- ●Map every redirect-prone endpoint before throwing payloads.
- ●When stuck, try IDN/Punycode and CRLF as last resorts.
- ●Always render the bypass URL in a real browser to confirm.
- ●For OAuth chains, capture the code on attacker.com in real time.
- ●Document the chain end-to-end.
Bug Bounty Tips
- ●Plain open redirects pay $0 to $500.
- ●Chained Open Redirect + OAuth ATO pays $3,000 to $50,000.
- ●Open Redirect + Stored XSS pays $2,000 to $20,000.
- ●Open Redirect + SSRF + cloud metadata pays $5,000 to $50,000.
- ●Lead with the chain, not the redirect.
- ●Include a video PoC for chains.
- ●Test marketing/tracking subdomains.
Red Team Notes
- ●Open Redirect on a brand domain is golden for phishing.
- ●Mail filters trust the start of the URL.
- ●Open Redirect + OAuth = ATO at scale.
- ●SAML RelayState abuse against IdPs is rarer but devastating.
Real-World Advice
- ●Browsers do NOT warn users about cross-domain redirects.
- ●The padlock stays green through the entire chain.
- ●URL filters in mail clients trust the first hop.
- ●User-awareness training does not save users from open-redirect phishing.
- ●The only defense is server-side validation.
Things to Remember During Exams
- ●CWE-601 ==> URL Redirection to Untrusted Site.
- ●`//attacker.com` is protocol-relative.
- ●`@` splits userinfo from host.
- ●OAuth + Open Redirect = ATO.
- ●Use exact-match `redirect_uri`, never `startsWith`.
- ●Django: `url_has_allowed_host_and_scheme`. ASP.NET: `LocalRedirect`. Rails: `allow_other_host: false`.
Things to Remember During Real Assessments
- ●Always confirm the redirect fires in a real browser, not just curl.
- ●Record both the request and the resulting Location: header.
- ●For OAuth chains, demonstrate code capture and token exchange end-to-end.
- ●Never test against production OAuth providers without permission.
- ●Use benign decoy pages for phishing impact demonstrations.
Frequently Confused Concepts
- ●Open Redirect vs CSRF. Redirect navigates the browser; CSRF makes the browser submit a request.
- ●Open Redirect vs SSRF. Redirect is client-side; SSRF is server-side fetching.
- ●Open Redirect vs Reflected XSS. Redirect changes location; XSS executes script. They chain via `javascript:` URIs.
- ●Open Redirect vs Phishing. Redirect is the primitive; phishing is the outcome.
- ●redirect_uri vs RelayState. OAuth uses `redirect_uri`; SAML uses `RelayState`. Both validated identically in spirit.
Interview Tips
- ●Explain Open Redirect without using "phishing" first.
- ●Cite CVE-2025-4123 (Grafana) and CVE-2024-52289 (Authentik) as recent examples.
- ●Mention CWE-601 and the OAuth chain.
- ●Finish with the defense: allowlist, host-parse, framework-safe API, exact-match redirect_uri.
- ●Draw the safe-vs-vulnerable code from memory.
Key Takeaways
- ●A redirect is the server's decision; attackers want to make it their decision.
- ●Plain Open Redirect is phishing. Chained Open Redirect is account takeover.
- ●The bypass arsenal is large (`//`, `/\`, `@`, `.attacker.com`, encoding, IDN, CRLF). The defense is small (allowlist, host-parse, exact-match).
- ●OAuth and SAML flows turn Open Redirect into the keys to the kingdom.
- ●The fix is one architectural rule: the destination must come from server-trusted data.
24. Final Word from Your Instructor
Open Redirect is the vulnerability that hides in plain sight.
It looks like a footnote in a security report. It feels like "the user got phished, not us". It scores low on the standard CVSS calculator unless someone bothers to model the chain.
But every OAuth account takeover ever shipped to production started with an Open Redirect.
Every SAML hijack started with a `?RelayState=` trusted blindly.
Every "phishing campaign that bypassed our DMARC and SPF" started with a legitimate brand domain that lent its trust to the attacker for free.
Every time a developer writes:
A new Open Redirect is born somewhere in the world.
Your job is to look at any `?next=`, `?returnTo=`, `?redirect_uri=`, `?RelayState=` and ask: "Who decides where this URL goes?"
When you see a login form, ask: "Is the post-login destination chosen by the server or by the user?"
When you see an OAuth flow, ask: "Is the `redirect_uri` exact-matched, or is it `startsWith`?"
When you see a SAML response, ask: "Is the RelayState a fixed token or a free-form URL?"
When you see a marketing redirect (`/r?url=...`), ask: "Is there any host validation at all?"
When you see a password-reset email, ask: "Could the click leak the token via Referer?"
If the answer takes you to a place where an attacker decides where a browser goes next, you have found a bug worth thousands of dollars.
The 30 bypasses are your arsenal. The 50 parameter names are your map. The OAuth chain is your endgame.
- ●Welcome to the world where a single URL parameter decides who owns the account.
- ●Go hunt.