Open redirect

Unvalidated user-selected redirect URLs may enable phishing or abuse of authentication flows.

Description

An open redirect occurs when an application uses an unvalidated user-supplied URL in a redirect response, such as redirect or sendRedirect. A link may begin with a legitimate domain but send the user to an external phishing or malicious download site.

Common locations include post-login navigation, an OAuth callback's returnUrl, and a page shown after payment.

Potential impact

  • Phishing through links that use a trusted service address
  • Token disclosure or session compromise when an authentication flow trusts an unvalidated redirect
  • Redirection to malicious downloads or vulnerable external sites
  • Bypassing filters by redirecting through a permitted domain

Remediation

  • Restrict destinations to paths or domains in a server-owned allow-list.
  • Prefer fixed identifiers such as next=profile, mapped to actual paths on the server, over accepting a complete URL.
  • If external URLs are necessary, validate the scheme, host, port and normalized result, and permit only https.
  • Do not rely on startsWith or contains checks. A value such as https://trusted.example.com.evil.example may bypass them.
  • Reject unapproved values or use a safe default path.

Examples

These Spring controller excerpts omit the required imports and application-defined internal routes.

Before

java
@GetMapping("/login/success")
public void loginSuccess(
    @RequestParam("next") String next,
    HttpServletResponse response
) throws IOException {
    response.sendRedirect(next);
}

After

java
private static final Map<String, String> ALLOWED_REDIRECTS = Map.of(
    "profile", "/account/profile",
    "orders", "/account/orders"
);

@GetMapping("/login/success")
public void loginSuccess(
    @RequestParam(value = "next", defaultValue = "profile") String next,
    HttpServletResponse response
) throws IOException {
    String redirectPath = ALLOWED_REDIRECTS.getOrDefault(next, "/account/profile");
    response.sendRedirect(redirectPath);
}

Explanation:

  • Before: Uses the request parameter directly as a redirect URL. A user clicking a legitimate service link may end up on an external site.
  • After: Treats input as a server-known identifier rather than a URL. Unrecognized values use a safe default path.

References