Uncontrolled external site redirect
Need
Restriction of redirect targets to paths within the application
Context
• Usage of Elixir 1.15+ for building application services
• Usage of Plug.Router for handling HTTP requests
Description
1. Non compliant code
defmodule MyAppWeb.LoginRouter do
use Plug.Router
plug :match
plug :fetch_query_params
plug :dispatch
get "/login/done" do...The `/login/done` route below sends the user to the value of the `next` query parameter with a `302` response and a `location` header, without checking it. An attacker can send a link such as `https://app.example.com/login/done?next=https://app-example.evil.com/login` by email or chat. The link points to the real domain, so it looks trustworthy, but the user ends up on a copy of the site controlled by the attacker, which asks for the password again or for payment details. Open redirects are also used to bypass URL allowlists in OAuth flows and to leak authorization codes.
2. Steps
• Never copy request data directly into the `location` header of a redirect or into `Phoenix.Controller.redirect(external:)`.
• Accept only relative paths that start with a single `/`, and reject values that start with `//` or `/\` or contain control characters.
• Map external destinations to identifiers that the server resolves against a fixed allowlist of URLs.
• Fall back to a default page when the requested target is not valid.
• Register exact redirect URIs for OAuth and single sign-on clients.
3. Secure code example
defmodule MyAppWeb.LoginRouter do
use Plug.Router
@default_target "/dashboard"
plug :match
plug :fetch_query_params
plug :dispatch...The corrected route passes the parameter through `safe_target/1` before redirecting. The function only accepts relative paths: the value must start with a single `/`. Values starting with `//` or `/\` are rejected because browsers treat them as protocol-relative URLs pointing to another host, and values with control characters are rejected because some browsers strip them and reinterpret the rest. Anything else, including absolute URLs and missing parameters, falls back to `/dashboard`. Because the target is always a path, the browser stays on the same origin. When the application must redirect to other domains, the parameter should carry an identifier that the server maps to a fixed URL instead of the URL itself.
References
• 156. Uncontrolled external site redirect