Open Redirect Vulnerabilities: Why Your Login Page Might Be Phishing Your Users

Open redirects are the most underrated vulnerability class in web security. They're consistently deprioritised in triage ("it's just a redirect"), they're excluded from many bug bounty scopes, and they're rarely flagged by automated scanners. And yet they're the foundation of some of the most effective phishing campaigns and the key ingredient in SSRF bypass chains.

Here's the problem in one URL:

https://your-bank.com/login?next=https://attacker.com/fake-login

Your user sees your-bank.com in the link. They click it. They land on a page that looks exactly like your login form. They enter their credentials. The credentials go to the attacker.

The redirect happened on your domain. Your SSL certificate was in the address bar. Your brand was in the URL. And your application sent the user to the attacker.

Where Open Redirects Hide

Open redirects appear wherever an application uses a URL parameter to control navigation:

LocationCommon ParametersImpact
Login pagesnext, redirect, return_to, continueCRITICAL โ€” credential phishing
OAuth callbacksredirect_uri, callbackCRITICAL โ€” token theft
Logout pagesredirect, nextMEDIUM โ€” phishing after logout
Marketing linksurl, dest, targetMEDIUM โ€” brand abuse
Error pagesreturn, backLOW โ€” limited phishing
File downloadsfile, path, downloadMEDIUM โ€” malware delivery

The Attack Chains

Chain 1: Phishing via Trusted Domain

The simplest and most effective use. The attacker crafts a URL using your domain that redirects to their phishing page. Email filters and link scanners see your trusted domain and let the link through. The user sees your domain and trusts it.

https://company.com/login?redirect=https://company-login.attacker.com

Chain 2: OAuth Token Theft

OAuth flows use a redirect_uri parameter to send the authorization code back to the application. If the OAuth provider doesn't strictly validate the redirect URI, an attacker can change it to their own server and steal the authorization code.

https://oauth-provider.com/authorize?
  client_id=legit-app&
  redirect_uri=https://legit-app.com/callback/../../../redirect?url=https://attacker.com&
  response_type=code

The authorization code โ€” which can be exchanged for an access token โ€” goes to the attacker.

Chain 3: SSRF Bypass

Many SSRF defences validate the URL before making the request: "only allow requests to api.example.com." An open redirect on api.example.com bypasses this:

# SSRF payload that passes URL validation
GET /fetch?url=https://api.example.com/redirect?next=http://169.254.169.254/latest/meta-data/

The URL validation sees api.example.com (allowed). The server follows the redirect to the metadata endpoint (not allowed, but the validation already passed).

Chain 4: XSS via JavaScript Protocol

If the redirect is implemented client-side (via window.location = param), the attacker can use the javascript: protocol:

https://app.com/redirect?url=javascript:alert(document.cookie)

This turns an open redirect into a reflected XSS vulnerability.

How to Test for Open Redirects

Manual Testing

# Test 1: External domain redirect
curl -s -o /dev/null -w "%{redirect_url}" \
  "https://target.com/login?next=https://attacker.com"

# Test 2: Protocol-relative URL (bypasses scheme checks)
curl -s -o /dev/null -w "%{redirect_url}" \
  "https://target.com/login?next=//attacker.com"

# Test 3: Backslash confusion (some parsers treat \ as /)
curl -s -o /dev/null -w "%{redirect_url}" \
  "https://target.com/login?next=https://attacker.com\@target.com"

# Test 4: URL encoding bypass
curl -s -o /dev/null -w "%{redirect_url}" \
  "https://target.com/login?next=https%3A%2F%2Fattacker.com"

# Test 5: JavaScript protocol (for client-side redirects)
# Check if the page sets window.location to the parameter value

Common Bypass Techniques

BypassPayloadWhat It Bypasses
Protocol-relative//attacker.comScheme validation (checks for http/https)
Backslashhttps://attacker.com\@target.comDomain validation (some parsers read target.com)
URL encoding%2F%2Fattacker.comLiteral string matching
Subdomainhttps://target.com.attacker.comSuffix-based domain validation
Tab/newlinehttps://attacker%09.comWhitespace-insensitive parsers
Data URIdata:text/html,<script>...</script>Scheme allowlists that don't include data:

How to Fix Open Redirects

Option 1: Allowlist (Best)

ALLOWED_REDIRECTS = {'/dashboard', '/profile', '/settings'}

def safe_redirect(url):
    if url in ALLOWED_REDIRECTS:
        return redirect(url)
    return redirect('/dashboard')  # Default fallback

Option 2: Same-Origin Only

from urllib.parse import urlparse

def safe_redirect(url):
    parsed = urlparse(url)
    # Only allow relative paths (no scheme, no host)
    if parsed.scheme or parsed.netloc:
        return redirect('/dashboard')
    # Block protocol-relative URLs
    if url.startswith('//'):
        return redirect('/dashboard')
    return redirect(url)

Option 3: Signed Redirect Tokens

For cases where you need to redirect to external URLs (e.g., marketing links), use signed tokens instead of raw URLs:

# Generate: /redirect?token=hmac_signed_url
# Verify: check HMAC before redirecting
# This prevents attackers from crafting arbitrary redirect URLs

What NOT to Do

Testing Checklist

#CheckExpected Result
1External URL in redirect parameterRedirect blocked or goes to default page
2Protocol-relative URL (//attacker.com)Redirect blocked
3Backslash confusion (https://evil\@target.com)Redirect blocked
4URL-encoded payloadRedirect blocked after decoding
5Subdomain confusion (target.com.evil.com)Redirect blocked
6JavaScript protocol (javascript:alert(1))Not executed
7Data URI (data:text/html,...)Redirect blocked
8OAuth redirect_uri manipulationStrict redirect_uri validation by OAuth provider

Bottom Line

Open redirects are the vulnerability that makes other vulnerabilities worse. On their own, they enable phishing. Combined with SSRF, they bypass URL validation. Combined with OAuth, they steal tokens. Combined with XSS, they execute arbitrary JavaScript.

The fix is simple: don't redirect to user-controlled URLs. Use allowlists for internal paths, signed tokens for external URLs, and never trust a URL parameter to be safe just because it starts with your domain.

See also: SSRF Detection (open redirects as SSRF bypass), XSS Detection (javascript: protocol via redirect), CORS Misconfiguration (redirect-based CORS bypass).

Advertisement