How unchecked redirect targets let attackers turn a trusted link into a phishing trap
An open redirect vulnerability arises when an application redirects a user to an arbitrary external URL based on a parameter value from the request, say `?next=` or `?returnUrl=`, without sufficient checking, instead of restricting the target to its own domain or an explicitly allowed list. On its own this looks harmless, but an attacker can use this vulnerability to send a link that visibly points to the trusted, real domain, but that actually redirects to an attacker-controlled phishing page after a harmless-looking intermediate step.
Table of Contents
- 1. Why a redirect alone is already a security risk
- 2. A typical vulnerable pattern in Symfony
- 3. The most robust fix: an explicit allowlist of permitted targets
- 4. Safe implementation with Symfony's UriSigner or route names
- 5. Open redirects as a building block in OAuth attack chains
- 6. Systematically testing for open redirects
- 7. A warning interstitial page as an additional protection layer
- 8. Logging and monitoring abuse attempts
- 9. Protective approaches at a glance
- 10. Summary
- 11. FAQ
1. Why a redirect alone is already a security risk
The actual value of an open redirect vulnerability for an attacker isn't a technical data leak, but abuse of trust: a link like `https://trusted-shop.com/logout?next=https://malicious-site.com` visibly starts with the real, familiar domain for the user, which renders classic phishing detection advice like "check the domain in the address bar" useless, since the user genuinely sees the correct domain at first, before the server-side redirect silently sends them to a different domain.
This property makes open redirects especially valuable for targeted phishing campaigns, since the malicious link often slips through email security filters that frequently only check the domain of the first visible URL undetected, and since even security-conscious users who actually pay attention to the domain are lulled into false security by the correct first part of the URL.
2. A typical vulnerable pattern in Symfony
The following example shows a commonly found pattern: after a successful login, the user is redirected to the URL they wanted to visit before authenticating, stored in the `redirect` query parameter. Without checking the target value, an attacker can populate this parameter with an arbitrary external URL.
<?php
declare(strict_types=1);
// VULNERABLE: redirect target accepted without checking
#[Route('/login-success', name: 'app_login_success')]
public function loginSuccess(Request $request): RedirectResponse
{
$target = $request->query->get('redirect', '/dashboard');
return new RedirectResponse($target);
}
// Attacker link: https://real-domain.com/login-success
// ?redirect=https://malicious-site.com/fake-login
3. The most robust fix: an explicit allowlist of permitted targets
The most reliable safeguard is to check redirect targets fundamentally against an explicit allowlist of known, permitted domains or, even stricter, against a fixed list of known, internal route names, instead of trying to exclude dangerous external URLs via a blocklist or pattern check. Blocklist approaches nearly always fail in practice against clever bypasses, say through alternative URL encodings, double slashes, or unusual protocol prefixes, while an allowlist is structurally impossible to bypass, since any URL not explicitly permitted is categorically rejected.
The safest approach is to not accept redirect targets as a free-form URL at all, but as a symbolic key that gets resolved internally to a fixed, code-defined route, so an attacker can never inject an actual URL, only at most an invalid key, which then falls back to a safe default target.
4. Safe implementation with Symfony's UriSigner or route names
For cases where a relative URL within the own application genuinely needs to be passed along, say "back to the last visited product page", it's enough to check that the URL is relative (doesn't start with `http://`, `https://`, or `//`) and doesn't contain an embedded, absolute URL via a detour like `/\evil.com`, combined with an explicit allowlist of permitted path patterns.
<?php
declare(strict_types=1);
// SAFE: only relative paths allowed, no scheme prefixes
#[Route('/login-success', name: 'app_login_success')]
public function loginSuccess(Request $request): RedirectResponse
{
$target = $request->query->get('redirect', '/dashboard');
$isSafeRelative = str_starts_with($target, '/')
&& !str_starts_with($target, '//')
&& !str_starts_with($target, '/\\');
if (!$isSafeRelative) {
$target = '/dashboard';
}
return new RedirectResponse($target);
}
5. Open redirects as a building block in OAuth attack chains
Open redirect vulnerabilities become especially critical in combination with OAuth or OpenID Connect flows, since an OAuth authorization server returns a sensitive access token or authorization code to the user via a redirect URI after successful authentication. If the own application contains an open redirect vulnerability, an attacker can use it as a stepping stone to get the OAuth authorization server to first send the sensitive token to the own, trusted domain, from where the open redirect gap then silently forwards it on to the attacker's domain.
This attack chain works even when the OAuth provider itself strictly checks the redirect URI against a registered list, since the registered, trusted redirect URI does genuinely belong to the own domain, but the subsequent, internal forwarding is no longer controlled by the OAuth provider at all. This is why an open redirect vulnerability in an application that is itself an OAuth client is considered considerably more critical than in an application without OAuth integration.
6. Systematically testing for open redirects
A systematic test searches the application code for all places where a `RedirectResponse` (or an equivalent mechanism) is created with a value coming from the request, say via a code search for `RedirectResponse` combined with `$request->query` or `$request->get`. For every found location, it should then be checked whether an external URL like `https://attacker-controlled-domain.com` is accepted as a redirect target and actually delivered as a 3xx redirect with this foreign domain in the `Location` header.
Automated security scanners like OWASP ZAP contain specialized check rules for open redirects that systematically try known bypass techniques (say `//attacker.com`, `/\attacker.com`, double URL-encoded slashes) against every detected redirect parameter, which is a good complement to manual code analysis.
7. A warning interstitial page as an additional protection layer
For use cases where a redirect genuinely needs to go to an external URL specified by the user or a third party, say affiliate links or external payment providers, an explicit interstitial page ("You are now leaving our website and will be redirected to [external-domain.com]") considerably reduces the risk, because the user sees the actual target domain before the redirect and the silent, immediate redirection that phishing attacks actually rely on is removed.
This interstitial page is not a replacement for technical target validation, but an additional, user-facing defense layer that's especially useful in cases where a complete allowlist of all legitimate external targets isn't practical for business reasons.
8. Logging and monitoring abuse attempts
Even with a robust allowlist, it's worth logging every rejected redirect attempt with a disallowed target in a structured way, since a sudden spike of such rejections for a given user or IP address can indicate an active reconnaissance attempt by an attacker deliberately probing for a not-yet-hardened redirect location in the application. A simple alerting rule that fires on several rejected redirect targets within a short time from the same source often gives an early hint of targeted attack attempts, long before an actually successful phishing incident gets reported.
This logging is also valuable for reconstructing after the fact whether a reported phishing incident actually ran through an open redirect vulnerability in the own application, which can be decisive for communicating with affected users and for prioritizing subsequent security measures.
9. Protective approaches at a glance
The table below compares the protective approaches against open redirects presented.
| Approach | Security | Limitation |
|---|---|---|
| Domain/route allowlist | Very high, structurally impossible to bypass | Requires maintaining the list |
| Only allow relative paths | High, covers most cases | Bypasses like //-prefixes possible |
| Symbolic key instead of URL | Very high | Larger implementation effort |
| Warning interstitial page | Medium, depends on the user | No technical protection, only awareness |
Mironsoft
Security audits, OWASP-compliant hardening, and secure architecture
Applications that actually hold up against a real attack attempt?
We review existing applications for classic OWASP vulnerabilities, insecure authentication, and missing input validation, then build an architecture that structurally reduces attack surface instead of just patching individual symptoms.
Security Audit
Systematically checking OWASP Top 10, auth flows, and input validation for vulnerabilities.
Secure Architecture
Building rate limiting, encryption, and access controls correctly from the ground up.
Incident Readiness
Establishing logging, monitoring, and response processes for when things go wrong.
10. Summary
Open Redirect: The Essentials at a Glance
Core idea
Unchecked redirect targets let a trusted link appear to lead to the own domain, but actually lead to a phishing page.
Best fix
Check redirect targets against an explicit allowlist or accept a symbolic key instead of a free-form URL.
OAuth risk
Open redirects can serve as a stepping stone in OAuth flows to forward sensitive tokens to attackers.
Testing
Code search for RedirectResponse combined with request parameters, complemented by automated scanners.