Safeguard
Application Security

Preventing open redirect vulnerabilities

Open redirect flaws turn trusted domains into phishing and OAuth-token-theft infrastructure. Here's how they work, how to find them, and how to fix them for good.

Bob
Application Security Engineer
7 min read

An open redirect vulnerability lets an attacker take a link on your legitimate domain — https://yourapp.com/login?next=... — and repoint it at a site they control, so the URL that shows up in a phishing email, Slack message, or SMS still reads yourapp.com right up until the redirect fires. CWE-601 has classified this flaw since 2006, and OWASP gave it its own Top 10 category in 2013 (A10: Unvalidated Redirects and Forwards). The bug is trivial to introduce — one unvalidated redirect_to, next, returnUrl, or continue parameter — and just as trivial for attackers to weaponize, because it turns your brand's domain reputation, SPF/DKIM-passing mail infrastructure, or OAuth login flow into free phishing infrastructure. In 2013, researcher Nir Goldshlager showed the flaw could go further than phishing: an open redirect in Facebook's legacy OAuth dialog let him hijack access tokens outright. This post covers how the flaw works, how attackers use it today, and how to close it before it ships.

What Is an Open Redirect Vulnerability, Exactly?

An open redirect vulnerability is a flaw where an application sends a user to a URL taken from untrusted input — usually a query parameter or POST body — without checking that the destination is one the application actually intends to send users to. It's tracked under CWE-601 ("URL Redirection to a Untrusted Site") and shows up anywhere a "return to this page after logging in" or "continue to partner site" flow exists. A typical vulnerable Express.js handler looks like this:

app.get('/login', (req, res) => {
  // ...authenticate user...
  res.redirect(req.query.next); // attacker controls req.query.next
});

An attacker sends https://yourapp.com/login?next=https://evil.example/phish. The victim sees yourapp.com in the link preview, in the SEG scan, in the hover tooltip — everything checks out until the 302 response fires and the browser lands on the attacker's page. The same pattern recurs in Django's HttpResponseRedirect(request.GET.get('next')), Spring's RedirectView, and PHP's header("Location: " . $_GET['url']).

How Do Attackers Actually Exploit an Open Redirect?

Attackers exploit open redirects mainly to bypass link-reputation filters, steal OAuth tokens, and occasionally to chain into server-side request forgery. The most common use is phishing evasion: secure email gateways (SEGs) and URL-reputation engines typically inspect the visible hostname in a link, not the final destination after redirects, so a link that starts at accounts.google.com, l.messenger.com, sendgrid.net, or pardot.com and bounces to a credential-harvesting page sails past filters that would have blocked a raw evil.example link outright. Security vendors including Check Point, Avanan, and INKY documented sustained campaigns between 2020 and 2023 abusing exactly this pattern against Salesforce Marketing Cloud, American Express, and Snapchat redirect endpoints. The second major abuse path is OAuth/OIDC token theft: RFC 6749 §10.6, published in 2012, explicitly warns that an authorization server allowing an open redirect at its redirect_uri endpoint lets an attacker capture authorization codes or access tokens meant for the legitimate client. Goldshlager's 2013 Facebook disclosure was exactly this — he found an unvalidated next parameter in a legacy OAuth dialog and used it to redirect the token-bearing response to a page he controlled.

Which Real-World Incidents Show the Impact of Open Redirects?

The clearest evidence is that even mature engineering organizations keep re-introducing this bug for over a decade, because the fix is easy to get almost-right and still be bypassable. Django's own changelog is a good record: its is_safe_url() helper — later renamed url_has_allowed_host_and_scheme() — was patched in security releases in 2015 to reject control characters attackers used to smuggle a redirect past the allow-list check, and again in 2019 after researchers found a separate bypass using malformed scheme prefixes. Each patch closed one bypass technique; each patch was followed, months later, by a new bypass technique. On the incident side, the 2013 Facebook case remains the textbook example precisely because the impact wasn't a phishing page — it was direct account takeover via a stolen OAuth token, demonstrating that "just a redirect" can be the entire authentication bypass depending on what sits downstream. More recently, threat intel from Microsoft's own Digital Crimes Unit and multiple SEG vendors has flagged abuse of Microsoft 365's login.microsoftonline.com and Office sway.office.com redirect parameters in credential-phishing kits distributed between 2020 and 2022, precisely because those domains carry the trust and TLS certificates attackers can't forge themselves.

How Do You Detect Open Redirect Vulnerabilities Before Attackers Do?

You detect open redirects by combining static analysis that traces untrusted input to redirect sinks with dynamic testing that fuzzes known redirect parameter names against a live instance. On the static side, the pattern to search for is any redirect(), Location header write, RedirectView, or sendRedirect() call whose argument traces back to a request parameter without passing through a hostname allow-list first — CodeQL, Semgrep, and most commercial SAST tools ship a rule for exactly this CWE-601 taint path. On the dynamic side, attackers and scanners both target a well-known short list of parameter names, so testing each of them against your own endpoints costs little and catches most instances fast:

  • next, redirect, redirect_to, redirect_uri, return, returnUrl
  • continue, dest, destination, redir, target, rurl
  • out, view, callback, checkout_url, login_url, image_url

Feed each one a payload like https://evil.example and a protocol-relative payload like //evil.example, then check the Location header of the response — not just the rendered page, since some frameworks redirect silently via JavaScript rather than a 3xx status code.

How Do You Fix an Open Redirect Once You've Found One?

You fix an open redirect by validating the destination against a positive allow-list of known-safe relative paths or hostnames — never by trying to blocklist "bad" URLs, since attackers have a long history of bypassing blocklists with tricks like leading whitespace, backslashes instead of forward slashes, javascript: schemes, double-encoding, or an @ character that makes https://yourapp.com@evil.example parse as a path on evil.example. The safest fix is to stop accepting absolute URLs at all and only allow relative paths:

app.get('/login', (req, res) => {
  const next = req.query.next;
  const safePath = next && next.startsWith('/') && !next.startsWith('//')
    ? next
    : '/dashboard';
  res.redirect(safePath);
});

Where a cross-domain redirect is a genuine requirement — SSO callbacks, partner checkout flows — validate the parsed hostname against an explicit allow-list of domains you control or contractually trust, and reject anything else, including subdomain wildcards you didn't intend (evil-yourapp.com is not yourapp.com). For OAuth/OIDC flows specifically, register exact redirect_uri values with the authorization server rather than allowing pattern matches, per the guidance in RFC 6749 §10.6 and its 2020 successor draft, OAuth 2.1.

How Safeguard Helps

Safeguard finds open-redirect sinks like these across your codebase and SBOM-tracked dependencies, then uses reachability analysis to tell you which ones are actually wired to attacker-controlled input on a live route versus dead code that never ships — so your team fixes the ones that matter first instead of triaging every res.redirect() call in the repo. Griffin AI, Safeguard's detection engine, traces the taint path from request parameter to redirect sink across service boundaries and framework abstractions that pattern-matching scanners miss, and flags the specific bypass class (protocol-relative, scheme-confusion, allow-list gap) at play. For dependencies that introduce redirect logic — auth SDKs, SSO middleware, webhook libraries — Safeguard's SBOM generation and ingest pipeline tracks which versions carry known CWE-601 advisories so you're not relying on manual changelog review. When a fix is available, Safeguard opens an auto-fix pull request with the allow-list or relative-path validation already applied and scoped to the exact call site, so remediation ships in minutes rather than sitting in a backlog.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.