Open Redirect vulnerabilities (CWE-601) occur when an application accepts untrusted input to construct redirection targets. While often dismissed as low-severity cosmetic bugs, parser differentials and protocol handling quirks can turn simple redirects into critical OAuth account takeovers, Cross-Site Scripting (XSS), and Server-Side Request Forgery (SSRF) bypasses.
Redirection mechanisms are fundamental to web applications—routing unauthenticated users to identity providers, processing post-checkout landing pages, and switching geographic localizations. When an endpoint trusts user-supplied parameters to determine the Location response header without rigorous validation, attackers can redirect victims to malicious destinations under the implicit trust of the legitimate domain.
CWE-601: URL Redirection to Untrusted Site ('Open Redirect') • CVSS v3.1: 4.7 – 8.1 (Variable based on token leakage and auth chains) • RFC 3986 Section 3: URI Generic Syntax Specification
The Anatomy of Vulnerable Endpoints
A typical vulnerable pattern involves handling query parameters like next, return_to, or redirect_uri without structural validation:
import { Request, Response } from 'express';
export function handleLoginRedirect(req: Request, res: Response) {
const target = req.query.return_url as string;
// FLAW: Direct header reflection without origin validation
if (target) {
return res.redirect(302, target);
}
return res.redirect('/dashboard');
}
Bypass Techniques & Parser Differentials
Developers often attempt to sanitize redirect parameters with flawed regular expressions or prefix checks. In production security audits documented on tirup.in, we repeatedly observe common parser differential bypasses:
| Vector / Payload | Bypass Mechanism | Target Parser Vulnerability |
|---|---|---|
//evil.com/login |
Protocol-relative URL | Browser interprets as https://evil.com |
/\evil.com |
Backslash normalization | WebKit/Blink normalize backslash to forward slash |
https://trusted.com@evil.com |
Userinfo ambiguity | Everything before @ parsed as credentials |
https://trusted.com.evil.com |
Prefix substring match | Naïve target.startsWith("https://trusted.com") |
javascript:alert(1) |
Scheme execution | DOM sinks evaluating window.location = target |
When authorization servers allow wildcard redirect URIs or loose subpath matching, an open redirect on the callback domain allows attackers to capture authorization codes or access tokens via the Referer header or URI fragment manipulation.
Defense in Depth: Deterministic Validation
The only reliable defense against open redirects is deterministic URL decomposition. Instead of regex blacklists, parse the URL using the standard WHATWG URL API and enforce strict origin allowlists or relative path guarantees:
const ALLOWED_ORIGINS = new Set([
'https://tirup.in',
'https://blogs.tirup.in',
]);
export function getSafeRedirectUrl(rawUrl: string | undefined, defaultPath = '/'): string {
if (!rawUrl || typeof rawUrl !== 'string') {
return defaultPath;
}
// Enforce relative paths safely
if (rawUrl.startsWith('/') && !rawUrl.startsWith('//') && !rawUrl.includes('\')) {
return rawUrl;
}
try {
const parsed = new URL(rawUrl);
// Strict scheme enforcement
if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') {
return defaultPath;
}
// Origin allowlist check
if (ALLOWED_ORIGINS.has(parsed.origin)) {
return parsed.toString();
}
} catch {
// Malformed URI string
return defaultPath;
}
return defaultPath;
}
- Never reflect raw query parameters directly into HTTP 3xx
Locationheaders. - Reject protocol-relative URLs (
//example.com) and backslash escape variants (/\). - Validate schemes explicitly against
https:to preventjavascript:anddata:execution. - Use cryptographic state parameters in OAuth flows to tie redirection destinations to authenticated sessions.