Vulnerability library  /  Open redirect

Open redirect

Open redirect

An open redirect lets untrusted input choose where a trusted application sends the user's browser. It is commonly abused for convincing phishing links and can become more serious when combined with authentication or authorization flows.

Also known as
Unvalidated redirect · Unvalidated forward · Arbitrary redirect
Typical severity · Low to High
Last updated
On this page: What it is
  1. 1 · What it is
  2. 2 · Why it matters
  3. 3 · Examples
  4. 4 · How to fix it
  5. 5 · How to test for it
  6. References
  7. Related

What it is#

Applications often redirect users after login, logout, checkout, or a completed workflow. An open redirect exists when a query parameter, form field, header, or stored value can select an untrusted destination.

Because the link begins with a legitimate domain, users and automated defenses may trust it. The browser then follows the application's redirect to a different site.

URL parsing is subtle. Scheme-relative URLs, backslashes, encoded delimiters, userinfo, mixed case, and multiple decoding passes can cause a string that looks local to resolve somewhere else.

Why it matters#

  • Create credible phishing links hosted under a trusted domain.
  • Bypass destination allowlists used by email gateways or security products.
  • Leak OAuth authorization codes, tokens, or sensitive query parameters when redirect URI validation is weak.
  • Bypass navigation or workflow assumptions in login and SSO flows.
  • Combine with server-side request behavior, header injection, or client-side script flaws to increase impact.
Typical severity

Usually low to medium as a standalone issue. It can become high when used in an OAuth, SSO, password-reset, payment, or token-bearing workflow. Actual severity depends on context: who is affected, what data or actions are reachable, and existing controls.

Examples#

✕ Vulnerablejavascript
// Vulnerable: any absolute or scheme-relative URL is accepted.
app.get('/login/complete', (req, res) => {
  res.redirect(req.query.next || '/app');
});
✓ Fixedjavascript
// Fixed: parse once and permit only local application paths.
const applicationOrigin = 'https://portal.example.com';

app.get('/login/complete', (req, res) => {
  const requested = String(req.query.next || '/app');
  const destination = new URL(requested, applicationOrigin);

  if (destination.origin !== applicationOrigin ||
      !destination.pathname.startsWith('/app/')) {
    return res.redirect('/app/');
  }

  res.redirect(destination.pathname + destination.search + destination.hash);
});

An even safer pattern uses a server-owned destination identifier such as next=dashboard rather than accepting a URL.

How to fix it#

1. Use fixed destinations. Redirect to a constant URL whenever the workflow does not genuinely require a dynamic destination.

2. Map short identifiers to routes. For example, map billing to /app/billing on the server. Reject unknown identifiers.

3. Parse and compare normalized URLs. When external redirects are required, allowlist exact HTTPS origins and approved path prefixes. Compare parsed scheme, hostname, effective port, and path—not raw strings.

4. Reject ambiguous syntax. Block credentials in URLs, scheme-relative destinations, backslashes, control characters, nested URLs, and values that change meaning after another decoding pass.

5. Protect identity flows separately. OAuth and OIDC redirect URIs should be pre-registered and matched exactly according to the provider's guidance. Do not use a general application redirect allowlist for authorization responses.

6. Do not place secrets in redirect URLs. Avoid session identifiers and sensitive data in query strings or fragments. Use short-lived, single-use server-side state.

7. Add an interstitial only when necessary. A clear external-link warning can reduce phishing risk, but it is weaker than destination validation and should not be the primary control.

8. Centralize redirect helpers. Provide one reviewed function and prohibit direct redirects from untrusted values elsewhere in the codebase.

How to test for it#

  • Identify parameters named next, return, redirect, url, continue, callback, and similar values in login, logout, error, and workflow endpoints.
  • Use controlled domains such as example.com to confirm whether absolute and scheme-relative destinations are accepted.
  • Test encoded delimiters, backslashes, userinfo, mixed case, trailing dots, nested URLs, and repeated decoding without using deceptive real-world domains.
  • Verify that local paths cannot escape their approved application prefix.
  • Test OAuth and SSO callbacks with unregistered test redirect URIs and confirm exact rejection.
  • Confirm that rejected destinations use a safe fixed fallback and do not echo the untrusted URL into HTML.
  • Add table-driven tests for every accepted and rejected URL form handled by the central redirect helper.

References#

Permalinklibrary.infinity8labs.com/open-redirect/JSON
Found this in Breachvane? Your finding links here for background. Evidence, affected assets and retest status are in your Breachvane ↗ (opens in a new tab) workspace. Need help fixing it? Talk to us.