Open redirect vulnerabilities in AI-generated auth flows
作者:Seaworthy · 2026年6月25日
Photo by Nick Fewings on Unsplash
Open redirect vulnerabilities in AI-generated auth flows
Here is a pattern I see constantly in AI-generated login flows:
GET /login?redirect=/dashboard
User logs in. Server validates credentials. Server reads the redirect query parameter. Server sends a 302 to wherever that parameter points. User lands where they expected.
It works. The problem is what happens when an attacker controls that parameter.
https://yourapp.com/login?redirect=https://evil.example.com/fake-login
Your server authenticates the user against your real credentials database, then helpfully sends them to the attacker's phishing page. The URL bar showed yourapp.com during login. The user had no reason to distrust it. By the time they notice the redirect, they may have already typed their credentials into the fake page.
This is an open redirect vulnerability. It is not exotic. It sits comfortably in OWASP's Top 10, and it is one of the most reliably generated security bugs I've seen come out of AI coding assistants.
Why AI assistants generate this pattern
LLMs are trained on enormous amounts of web code, much of which implements post-login redirects without any validation. The pattern is simple, functional, and incredibly common. When you ask an AI to "add a redirect back to the page the user was on after login," it will produce something like this:
const redirectTo = req.query.redirect as string;
res.redirect(redirectTo || '/dashboard');
That's it. No validation. No allowlist. Works perfectly for the happy path, and that's all the AI was optimizing for.
Some assistants will add a URL decode step, making the situation worse because it opens the door to double-encoding bypasses. A few will mention that "you should validate this in production," then leave you to figure out what that means.
How to fix it
The fix has two parts: decide what a valid redirect looks like, and enforce it.
For most applications, a valid post-login redirect is a path on your own domain. Absolute URLs to external hosts should be rejected outright. Relative paths starting with / are usually safe, but you still need to watch for protocol-relative URLs (//evil.example.com) which are parsed as absolute by browsers.
function isSafeRedirect(url: string): boolean {
// Reject empty or obviously wrong values
if (!url || typeof url !== 'string') return false;
// Protocol-relative URLs are absolute URLs in disguise
if (url.startsWith('//')) return false;
// Reject anything with a scheme (http://, https://, javascript:, etc.)
if (/^[a-zA-Z][a-zA-Z0-9+\-.]*:/.test(url)) return false;
// At this point we have a relative path — ensure it starts with /
// to avoid path traversal tricks like "../admin"
if (!url.startsWith('/')) return false;
return true;
}
// Usage in your login handler
const raw = req.query.redirect as string;
const redirectTo = isSafeRedirect(raw) ? raw : '/dashboard';
res.redirect(redirectTo);
If you genuinely need to redirect to external domains (some SSO setups do), use an explicit allowlist rather than trying to parse and validate arbitrary URLs. URL parsing is hard. Attackers have had decades to find edge cases in every language's URL parser.
const ALLOWED_REDIRECT_HOSTS = new Set([
'app.yourcompany.com',
'admin.yourcompany.com',
]);
function isSafeExternalRedirect(url: string): boolean {
try {
const parsed = new URL(url);
return (
(parsed.protocol === 'https:') &&
ALLOWED_REDIRECT_HOSTS.has(parsed.hostname)
);
} catch {
return false;
}
}
Note the explicit check for https: only. Redirecting to http:// URLs from a post-auth flow is its own category of problem.
The validation gaps that bite people
A few things that look like validation but aren't:
Checking that the URL "starts with" your domain string is not safe. https://yourapp.com.evil.com starts with https://yourapp.com.
Checking that the URL "contains" your domain string is not safe. https://evil.com/?ref=yourapp.com contains your domain.
Using URL.hostname === 'yourapp.com' without also checking the protocol is not safe. javascript:alert(1) has no hostname, but javascript: URIs in redirect targets have been used in the wild.
The isSafeRedirect function above rejects the problem at the scheme level rather than trying to parse and compare hostnames. For same-origin redirects that is the right approach. Let the browser construct the full URL from the relative path; don't do it yourself.
Where to look in your own codebase
Search for these patterns:
req.query.redirect,req.query.returnUrl,req.query.next,req.query.return_to- Any
res.redirect()orresponse.redirect()call where the argument is not a string literal - OAuth callback handlers that forward state parameters
Auth flows written by AI assistants are the most likely place to find this, but it appears in hand-written code too. Any time a developer wants to preserve navigation state across a login boundary, the temptation is to stuff a URL in a parameter and trust it on the way out.
Seaworthy's configuration check category flags unvalidated redirect parameters in auth route handlers as part of its static analysis pass.
本文由 AI 生成,总结了公开可用的来源。
Seaworthy 会扫描你的代码仓库,发现类似本文提到的各种问题。
安全漏洞、泄露的密钥和配置错误——在部署前捕获。免费运行,无需账户。