Redirect target comes from the caller
The destination of a redirect is taken from the request without being checked. An attacker can send a link that starts with your domain and ends on theirs, which is what makes a phishing page credible - and in an OAuth callback it hands the authorisation code to whoever asked. Resolve the target against your own origin and refuse anything else.
medium likely application source OWASP A01:2021 / CWE-601
What it looks like
From app/api/proxy/route.ts in the fixture suite.
The fixture test asserts this finding.
How to fix it
Resolve the target against your own origin and refuse anything that lands elsewhere. Do not use a startsWith('/') check: '//evil.com' passes it and leaves the site.
// lib/safe-redirect.ts
export function safeRedirect(target: unknown, base: string, fallback = '/'): string {
if (typeof target !== 'string') return fallback
try {
const resolved = new URL(target, base)
// Same origin only. This rejects '//evil.com', 'https://evil.com',
// and 'javascript:' alike. A leading-slash test does not: the browser
// reads '//evil.com' as a URL to another host.
return resolved.origin === new URL(base).origin ? resolved.pathname + resolved.search : fallback
} catch {
return fallback
}
}
The fix for your framework
Choose the API used by your project.
- Next.js
- Nuxt
- NestJS
- Express
- Fastify
- Hono
- Koa
- Hapi
- Sails.js
- Astro
- Remix
- Gatsby
- SvelteKit
- TanStack Start
- SolidStart
- Elysia
Check your own repository
npx owlwarden scan --preset deep
npx owlwarden explain open-redirect
explain prints the rule and fixes in the terminal. It does not
use the network.