An open redirect occurs when a web application takes user input and uses it to redirect the browser to another URL without validating that the destination is…
An open redirect occurs when a web application takes user input and uses it to redirect the browser to another URL without validating that the destination is safe. Attackers exploit this to trick users into visiting malicious sites by disguising the malicious link as a redirect from a trusted domain. This is particularly dangerous because users often trust links from legitimate sites and may not notice the redirect.
02How It Happens
Web applications commonly redirect users for legitimate reasons: after login, after form submission, or when a user clicks a "return to previous page" link. Developers often accept a URL parameter (like redirect_to, return_url, or next) to control where the user goes next. If this parameter is used directly in a redirect without checking whether the destination is on the same domain or in an allowlist, an attacker can supply a malicious URL instead. The browser will faithfully follow the redirect, and the user may believe they are still interacting with the trusted site.
03Real-World Impact
Open redirects are frequently used in phishing campaigns. An attacker sends a user a link like https://trusted-site.com/login?next=https://attacker.com/fake-login, which appears to come from the legitimate domain. When the user clicks it, they are redirected to a fake login page that looks identical to the real one, where they unknowingly enter their credentials. The attacker captures these credentials and gains account access. Open redirects can also be chained with other vulnerabilities or used to distribute malware.
Why it's vulnerable: The next parameter is taken directly from user input and passed to redirect() without any check. An attacker can set next to any URL, including a malicious external site.
Fixed pattern
from flask import Flask, request, redirect
from urllib.parse import urlparse
app = Flask(__name__)
ALLOWED_HOSTS = ['trusted-site.com', 'www.trusted-site.com']
@app.route('/login', methods=['POST'])
def login():
username = request.form.get('username')
password = request.form.get('password')
if validate_user(username, password):
redirect_url = request.args.get('next', '/')
# Validate: only allow relative URLs or same-domain URLs
if redirect_url.startswith('/'):
return redirect(redirect_url)
parsed = urlparse(redirect_url)
if parsed.netloc in ALLOWED_HOSTS:
return redirect(redirect_url)
# Default to home if redirect is unsafe
return redirect('/')
return "Login failed", 401
Why it's vulnerable: The next parameter is inserted directly into the Location header without checking whether it points to an external site or a trusted destination.
Fixed pattern
<?php
// login.php
$allowed_hosts = ['trusted-site.com', 'www.trusted-site.com'];
if (validate_user($_POST['username'], $_POST['password'])) {
$redirect_url = $_GET['next'] ?? '/';
// Allow relative URLs (start with /)
if (strpos($redirect_url, '/') === 0) {
header("Location: $redirect_url");
exit;
}
// Allow same-domain absolute URLs
$parsed = parse_url($redirect_url);
if (isset($parsed['host']) && in_array($parsed['host'], $allowed_hosts)) {
header("Location: $redirect_url");
exit;
}
// Default to home if redirect is unsafe
header("Location: /");
exit;
}
?>
05Prevention Checklist
Use relative URLs by default. If a redirect parameter is needed, prefer relative paths (e.g., /dashboard) over absolute URLs.
Maintain an allowlist of safe destinations. If absolute URLs are necessary, define a whitelist of trusted domains and validate against it before redirecting.
Reject external URLs. If a redirect parameter points to a different domain, reject it and redirect to a safe default (usually the home page or dashboard).
Validate the URL structure. Use a URL parsing library to extract the hostname and compare it against your allowlist, not string matching.
Document redirect parameters. Clearly mark any user-controlled redirect parameters in code comments so future maintainers understand the security requirement.
Test with malicious URLs. Include test cases that attempt to redirect to external sites (e.g., https://attacker.com, //attacker.com) to verify your validation works.
06Signs You May Already Be Affected
Check your application logs for unusual redirect parameters in URLs. Look for next, redirect, return_url, or similar parameters that contain external domains or suspicious-looking URLs. If you find evidence of such parameters being used without validation, audit all redirect code paths immediately. Additionally, monitor for user reports of unexpected redirects or phishing complaints that reference your domain.