← Debugging Guides

阅读中文版本

Open Redirect — Unvalidated Redirect Target

Fix open redirects by validating redirect targets against an allowlist, allowing only relative paths, and avoiding reflecting the request parameter.

intermediatedebuggingsecurityopen-redirectowasp

Symptoms

  • Visiting /login?next=https://evil.example.com redirects off-site
  • A security scanner reports an open redirect finding
  • The same parameter is used as a return URL after login

Possible causes

  • The redirect target comes from a request parameter without validation
  • A check allows any URL starting with the host string but not the actual origin
  • Protocol-relative or encoded URLs bypass a naive allowlist

Diagnosis steps

Reproduce with an external target

curl -i "https://app.example.com/login?next=https://evil.example.com" | grep -i location

Interpretation

A Location header pointing off-origin proves the redirect is unvalidated.

Locate redirect sinks

grep -rn "redirect(\|res.redirect\|Location" src/

Interpretation

Every place a request value controls the redirect target is a candidate to guard.

Root cause

When the redirect target is taken from user input and used directly, an attacker crafts a link that appears to be on your domain but redirects elsewhere, enabling phishing and token theft.

Fix

Allow only same-origin relative paths (reject anything with a scheme or host), or validate the target against a strict allowlist of known destinations before redirecting.

Risk note

String prefix checks like startsWith("https://[email protected]") are unsafe; parse the URL and compare the origin exactly.

Verify the fix

Resend the external target and confirm it is rejected or replaced with a safe default; verify a legitimate relative path still redirects.

Symptoms

The error “Open Redirect — Unvalidated Redirect Target” points at a specific failure mode. This guide documents the symptoms you can observe, the commands that isolate the cause, the root cause, the fix, and the checks that prove the fix works.

Observed symptoms include:

  • Visiting /login?next=https://evil.example.com redirects off-site
  • A security scanner reports an open redirect finding
  • The same parameter is used as a return URL after login

Reproduce and Diagnose

Reproduce the failure in isolation before changing any code. Work through the checks below in order; each one narrows the search space.

Step 1: Reproduce with an external target

curl -i "https://app.example.com/login?next=https://evil.example.com" | grep -i location

A Location header pointing off-origin proves the redirect is unvalidated.

Step 2: Locate redirect sinks

grep -rn "redirect(\|res.redirect\|Location" src/

Every place a request value controls the redirect target is a candidate to guard.

Likely contributors, in order of frequency:

  • The redirect target comes from a request parameter without validation
  • A check allows any URL starting with the host string but not the actual origin
  • Protocol-relative or encoded URLs bypass a naive allowlist

Root Cause

When the redirect target is taken from user input and used directly, an attacker crafts a link that appears to be on your domain but redirects elsewhere, enabling phishing and token theft.

The Fix

Allow only same-origin relative paths (reject anything with a scheme or host), or validate the target against a strict allowlist of known destinations before redirecting.

Watch out: String prefix checks like startsWith(“https://[email protected]”) are unsafe; parse the URL and compare the origin exactly.

function safeRedirectTarget(next, fallback = '/') {
  if (!next) return fallback;
  try {
    const url = new URL(next, 'https://app.example.com');
    // allow only same-origin relative paths
    if (url.origin !== 'https://app.example.com') return fallback;
    return url.pathname + url.search + url.hash;
  } catch {
    return fallback;
  }
}

Verify and Prevent

Resend the external target and confirm it is rejected or replaced with a safe default; verify a legitimate relative path still redirects.

Once verified, add a regression test that reproduces the original symptom so the failure cannot silently return.

This class of failure is usually environmental rather than pure logic, so capture the exact command, the environment variables, and the working directory in your bug report. Reproduce it in the smallest possible script, then widen the scope only once the minimal case is understood. A short written checklist for this failure mode saves hours the next time it appears.