Open Redirect in Laravel with Jwt Tokens
Open Redirect in Laravel with Jwt Tokens — how this specific combination creates or exposes the vulnerability
An open redirect occurs when an application redirects a user to an arbitrary URL without proper validation. In Laravel, this risk can intersect with JWT-based authentication in ways that expose both standard web flows and token-based API flows. When a Laravel application uses JWT tokens for authentication but also provides endpoints that accept redirect targets (for example, a post-login landing page, an OAuth callback, or a dynamic URL parameter), an attacker can supply a malicious URL as the redirect destination.
Consider a route designed to log in a user and then redirect to a provided next parameter. If the application does not validate or strictly whitelist allowed redirect hosts, an attacker can craft a URL such as /login?next=https://evil.com. After a successful JWT issuance, the application might issue the token and then redirect the user’s browser to the attacker-controlled location while still exposing a token-based session or embedding a token in a query parameter or fragment. This becomes especially dangerous when the JWT is returned in a URL fragment or query parameter that gets logged in browser history, proxy logs, or referrer headers, enabling token leakage alongside the open redirect.
With JWT tokens, the exposure is compounded when authorization checks are performed on the server before issuing the token but the redirect itself occurs afterward without re-validating the intended destination. For SPAs or mobile apps using Laravel as an identity provider, an open redirect can trick users into following a link that silently passes a JWT to an external site via URL parameters, or it can be used in phishing flows where the attacker combines a legitimate-looking host path with a malicious redirect target.
Even when Laravel uses middleware to guard routes, an open redirect in a public endpoint can undermine trust in token-based flows if the redirect occurs after authentication rather than before it. Attackers may leverage social engineering to send links that both authenticate the user via JWT and redirect them to a malicious site, creating a scenario where users believe they are on a trusted domain but are actually handing control to an attacker.
Jwt Tokens-Specific Remediation in Laravel — concrete code fixes
To mitigate open redirect risks in Laravel when working with JWT tokens, validate and constrain redirect targets rigorously and avoid passing tokens in URLs. Below are concrete remediation approaches and code examples.
- Validate redirect URLs against a strict allowlist of trusted hosts. Do not rely on parsing or domain comparison alone; use PHP’s
parse_urland a strict list of permitted domains.
use Illuminate\Http\Request;
use Illuminate\Support\Facades\URL;
use Illuminate\Support\Str;
function isValidRedirect(string $url, array $allowedHosts): bool
{
$parsed = parse_url($url);
if (!isset($parsed['host'])) {
return false;
}
$host = $parsed['host'];
// Reject URLs with non‑HTTP(S) schemes
$scheme = isset($parsed['scheme']) ? strtolower($parsed['scheme']) : '';
if (!in_array($scheme, ['http', 'https'], true)) {
return false;
}
return in_array($host, $allowedHosts, true);
}
// Usage in a controller
public function login(Request $request)
{
$allowed = ['app.example.com', 'dashboard.example.com', 'app.middlebrick.example'];
$next = $request->input('next');
$redirectTo = $next && isValidRedirect($next, $allowed) ? $next : route('home');
// Perform JWT-based authentication here
// ... obtain or create $token
return redirect($redirectTo);
}
- Avoid placing JWTs in URLs. Return tokens in response bodies or secure, HttpOnly cookies, and use short-lived tokens with refresh token rotation. If a token must be passed in a URL (for example in a native app deep link), ensure the redirect is validated and the token is immediately exchanged server‑side for a session or short‑lived credential.
// Example: Return JWT in JSON body instead of redirect with token in URL
public function authenticated(Request $request)
{
$user = $request->user();
$token = $user->createToken('api-token')->plainTextToken;
return response()->json([
'access_token' => $token,
'token_type' => 'Bearer',
'expires_in' => 3600,
]);
}
- Use Laravel’s built-in URL signature validation when generating signed URLs for redirects, and enforce referrer and origin checks for sensitive actions.
// Generate a signed redirect URL
$url = URL::signedRoute('callback', ['user' => $user->id]);
// In the callback controller, verify the signature and validate the host
public function callback(Request $request)
{
if (! URL::hasValidSignature($request)) {
abort(403, 'Invalid signature');
}
$allowed = ['app.example.com'];
if (! in_array($request->getHost(), $allowed, true)) {
abort(403, 'Host not allowed');
}
// Proceed safely
}
- Enforce SameSite cookies and secure flag for JWT storage when using cookies, and consider using the
encryptandmacfeatures for any serialized redirect state.
// config/session.php
return [
'cookie' => 'session_name',
'secure' => env('SESSION_SECURE', true),
'same_site' => 'strict',
];