Open Redirect Prevention
Understand how open redirect vulnerabilities enable phishing attacks and how to eliminate them with allowlist validation, relative-only redirects, and safe URL parsing.
An open redirect occurs when an application takes a user-supplied URL parameter and redirects the browser to it without validation. The redirect originates from a trusted domain, which makes phishing attacks far more convincing. A victim sees a link to yourcompany.com in their email, clicks it, and is silently sent to a lookalike page controlled by an attacker.
Why Open Redirects Are Dangerous
A phishing email containing https://yourcompany.com/login?next=https://evil.com/fake-login appears entirely legitimate. The visible domain is yours. Email security filters may not flag it because the link points to a real, reputable site. The user trusts the URL, clicks it, and lands on a page designed to steal their credentials.
Open redirects also enable:
- OAuth token theft: An attacker sets the redirect URI to their server and captures authorization codes or tokens.
- SSRF amplification: Combined with server-side request forgery, an open redirect can bounce internal requests to arbitrary destinations.
- Reputation damage: Your domain becomes a relay for malicious campaigns, potentially landing on blocklists.
The combination of open redirects and email spoofing is particularly effective. If an attacker can also spoof the sender address to match your domain (a separate email authentication issue), the entire flow -- from inbox to credential theft -- appears to come from your organization. Open redirects are among several common security misconfigurations that compound into severe attack chains when left unaddressed.
Common Vulnerable Parameters
Applications frequently use URL parameters to control post-action redirects. These parameter names are the most commonly exploited:
url,redirect,redirect_url,redirect_urinext,return,return_to,returnUrlgoto,continue,dest,destinationforward,target,redir,callback
Any parameter whose value is used in a Location header or window.location assignment is a potential open redirect if it accepts arbitrary URLs.
Fix 1: Allowlist-Based Validation
The most reliable fix. Maintain an explicit list of permitted redirect destinations and reject everything else:
// JavaScript / Node.js
const ALLOWED_HOSTS = new Set([
'yourcompany.com',
'app.yourcompany.com',
'accounts.yourcompany.com',
]);
function safeRedirect(redirectUrl, fallback = '/') {
try {
const parsed = new URL(redirectUrl, 'https://yourcompany.com');
if (ALLOWED_HOSTS.has(parsed.hostname)) {
return parsed.toString();
}
} catch (e) {
// Invalid URL — fall through to fallback
}
return fallback;
}
# Python
from urllib.parse import urlparse
ALLOWED_HOSTS = {'yourcompany.com', 'app.yourcompany.com'}
def safe_redirect(redirect_url: str, fallback: str = '/') -> str:
try:
parsed = urlparse(redirect_url)
if parsed.hostname in ALLOWED_HOSTS:
return redirect_url
except Exception:
pass
return fallback
The fallback ensures that invalid or disallowed URLs always send the user somewhere safe rather than triggering an error.
Fix 2: Relative-Only Redirects
If your application only needs to redirect within itself, reject any URL containing a scheme or hostname. Only allow paths that start with / and do not start with //:
function isRelativePath(url) {
// Must start with exactly one slash, not two
// Reject protocol-relative URLs like //evil.com
if (url.startsWith('//')) return false;
if (!url.startsWith('/')) return false;
// Parse and verify no scheme or host crept in
try {
const parsed = new URL(url, 'https://placeholder.local');
if (parsed.hostname !== 'placeholder.local') return false;
} catch {
return false;
}
return true;
}
function relativeRedirect(userInput, fallback = '/') {
return isRelativePath(userInput) ? userInput : fallback;
}
The //evil.com check is critical. Browsers interpret //evil.com/path as a protocol-relative URL, redirecting to evil.com while inheriting the current page's scheme. This is the most common bypass for naive relative-path checks.
from urllib.parse import urlparse
def is_safe_relative(url: str) -> bool:
if not url.startswith('/') or url.startswith('//'):
return False
parsed = urlparse(url)
return parsed.scheme == '' and parsed.netloc == ''
def relative_redirect(user_input: str, fallback: str = '/') -> str:
return user_input if is_safe_relative(user_input) else fallback
Fix 3: URL Parsing With Hostname Validation
When you must accept full URLs (for example, OAuth callback flows), always parse the URL and verify the hostname component:
function validateRedirectUrl(url) {
let parsed;
try {
parsed = new URL(url);
} catch {
return null; // not a valid URL
}
// Only allow HTTPS
if (parsed.protocol !== 'https:') return null;
// Check hostname against allowlist
if (!parsed.hostname.endsWith('.yourcompany.com') &&
parsed.hostname !== 'yourcompany.com') {
return null;
}
return parsed.toString();
}
Be cautious with endsWith checks. An attacker could register notyourcompany.com which ends with yourcompany.com. For subdomain matching, verify that the character immediately before the match is a dot or the hostname matches exactly.
Common Bypass Techniques to Defend Against
Attackers use encoding and URL quirks to bypass naive validation:
- Protocol-relative URLs:
//evil.com-- bypasses checks that only look forhttp://orhttps://. - URL encoding:
%2F%2Fevil.comdecodes to//evil.com. Always decode before validation. - Backslash confusion:
\/evil.comor\evil.com-- some parsers treat backslash as a path separator, some do not. Browsers may interpret it as a scheme-relative URL. - Userinfo abuse:
https://yourcompany.com@evil.com-- the part before@is treated as the userinfo component. The actual hostname isevil.com. Always use a proper URL parser rather than string matching. - Data and JavaScript URIs:
javascript:alert(1)ordata:text/html,...-- reject any scheme other thanhttps(andhttpif truly necessary).
A robust validation function must handle all of these. String operations like startsWith('/') or includes('yourcompany.com') are insufficient on their own. Always use a URL parser and validate the parsed components. Open redirects are closely related to other web vulnerabilities like CSRF, where user input manipulation leads to unintended actions.
Framework Integration
Most web frameworks provide built-in redirect safety:
- Django:
django.utils.http.url_has_allowed_host_and_scheme()validates redirect targets againstALLOWED_HOSTS. - Rails:
redirect_towithallow_other_host: false(Rails 7+) blocks external redirects by default. - Spring Security:
SavedRequestAwareAuthenticationSuccessHandlervalidates redirect targets automatically. - Next.js: Use
next/navigationredirect()with relative paths only. Avoid passing user input directly toredirect().
Verification
Test your redirect endpoints by passing external URLs in every redirect parameter. The application should either redirect to the fallback or return an error -- never follow the external URL. Run a CyberShield scan to detect open redirect vulnerabilities across your application's endpoints. Open redirects are one of several issues that passive web vulnerability assessment can identify without sending any exploit payloads.
Continue Reading
WAF Detection and Fingerprinting: How CyberShield Identifies Web Application Firewalls
Understanding what web application firewall protects a target is essential context for any security assessment. Learn how CyberShield passively fingerprints 15+ WAF vendors through header analysis, error patterns, and behavioral signatures.
Web Vulnerability Assessment: What Passive Analysis Reveals Without Firing a Single Exploit
Passive web analysis uncovers OWASP-relevant vulnerabilities -- information leaks, form weaknesses, exposed files, and redirect flaws -- without touching a single exploit.
HTTP Security Headers: The Complete Hardening Guide
Most web servers ship with minimal security headers. Learn which headers protect against XSS, clickjacking, MIME sniffing, and other browser-side attacks — and how to configure them correctly.