| name | prevent-open-redirect |
| description | Use when building any endpoint that redirects users based on a URL parameter — login redirects, logout returns, OAuth callback handling, or any redirect-after-action flow. |
| source | OWASP Unvalidated Redirects and Forwards Cheat Sheet (owasp.org/www-project-cheat-sheets); OWASP Top 10 2021 A01; CWE-601 |
| tags | ["security","owasp","open-redirect","unvalidated-redirect","phishing","web","developer"] |
Prevent Open Redirect
Validate redirect destination URLs against an allowlist of permitted domains or paths — never blindly redirecting to a URL parameter — to prevent phishing and OAuth token theft via open redirectors.
Why This Is Best Practice
Adopted by: CWE-601 (URL Redirection to Untrusted Site) is a Common Weakness Enumeration entry flagged by every major SAST tool (Checkmarx, Veracode, Semgrep). OWASP Top 10 2021 A01 (Broken Access Control) includes open redirects. Google, Twitter, and PayPal have all had reported open redirect vulnerabilities. Google Bug Bounty pays for open redirectors used in OAuth flows. HackerOne reports open redirects are consistently in the top vulnerability classes submitted.
Impact: Open redirectors in OAuth callback endpoints are a primary exploitation technique — an attacker sends a crafted authorization URL where redirect_uri points to https://legitimate-app.com/redirect?next=https://attacker.com, and the authorization server (if redirect_uri validation is weak) sends the authorization code to the attacker's site via the chain of redirects. Non-OAuth open redirectors enable highly credible phishing: https://your-bank.com/logout?next=https://attacker-bank.com appears to originate from the bank's domain.
Why best: Sanitizing the URL by stripping protocols or checking for specific patterns fails against IDN homograph attacks, double encoding (%2f%2f), and browser-specific quirks. An allowlist of known-safe domains is the only reliable defense.
Sources: OWASP Unvalidated Redirects Cheat Sheet; CWE-601; Google Bug Bounty program; OWASP Top 10 2021
Steps
-
Allowlist permitted redirect destinations — for same-app redirects, restrict to relative URLs or your own domain:
from urllib.parse import urlparse, urljoin
ALLOWED_HOSTS = {'app.example.com', 'www.example.com'}
def safe_redirect_url(next_url, fallback='/'):
if not next_url:
return fallback
parsed = urlparse(next_url)
if not parsed.scheme and not parsed.netloc:
return urljoin('/', next_url)
if parsed.scheme in ('http', 'https') and parsed.netloc in ALLOWED_HOSTS:
return next_url
return fallback
@app.route('/login', methods=['POST'])
def login():
next_url = request.args.get('next', '/')
return redirect(safe_redirect_url(next_url))
-
For same-site redirects: restrict to relative paths only:
import re
def ():
re.(, url, re.IGNORECASE):
url.startswith():
():
next_url is_safe_relative_url(next_url):
next_url
fallback
Rules
//attacker.com/path is a protocol-relative URL treated as https://attacker.com/path — always check for // prefix in addition to http://.
- IDN homograph attacks (
аpple.com with Cyrillic 'а') bypass domain string matching — normalize to punycode before comparing: app.example.com == app.xn--example-abc.com.
javascript: URLs can appear in redirect parameters — block any non-http/https scheme.
- Logout redirects are a primary target — attackers send users to attacker-controlled sites after logout to re-capture credentials.
Common Mistakes
- Checking
startswith('https://app.example.com') for the full URL — https://app.example.com.attacker.com/ passes this check.
- Allowing redirects to
localhost or 127.0.0.1 — enables SSRF-via-redirect if the redirect is followed server-side.
- Trusting the
Referer header to validate redirects — it can be spoofed and is often absent.
- Forgetting about
forward operations — server-side internal forwards can also be redirected to unauthorized internal paths.