Rules Hub
Coding Rules Library
Rule priority, scope & exceptions
Use this to align rules with the senior-level structure (P0/P1/P2, scope, exceptions/tradeoffs).
Validate redirect destinations against an allowlist; never redirect to raw user input
Server redirects and client navigations must validate the target against an allowlist or accept only same-origin relative paths — never redirect to an unchecked user-supplied URL.
Bad example
| 1 | // pages/api/login.ts — classic open redirect |
| 2 | export default function handler(req: NextApiRequest, res: NextApiResponse) { |
| 3 | const { returnTo } = req.query as { returnTo: string }; |
| 4 | // ?returnTo=https://evil.example.com/phish or //evil.example.com |
| 5 | res.redirect(returnTo); // redirects anywhere the attacker wants |
| 6 | } |
Explanation (EN)
Redirecting to an unvalidated parameter lets an attacker craft a link on your trusted domain that bounces victims to a phishing site, and on auth flows it can leak tokens/codes to an attacker-controlled host. Note //evil.example.com is treated as a protocol-relative absolute URL, not a path.
Objašnjenje (HR)
Preusmjeravanje na nevalidiran parametar omogućuje napadaču da napravi link na vašoj pouzdanoj domeni koji žrtve odbacuje na phishing stranicu, a u auth tokovima može procuriti tokene/kodove na napadačev host. Napomena: //evil.example.com se tretira kao protocol-relative apsolutni URL, ne kao path.
Good example
| 1 | function safeReturnPath(returnTo: unknown): string { |
| 2 | if (typeof returnTo !== 'string') return '/'; |
| 3 | // Accept only same-origin relative paths: must start with a single '/' |
| 4 | // and not with '//' or '/\\' (protocol-relative / backslash tricks). |
| 5 | if (!returnTo.startsWith('/') || returnTo.startsWith('//') || returnTo.startsWith('/\\')) { |
| 6 | return '/'; |
| 7 | } |
| 8 | // Normalize and confirm it still resolves on our own origin. |
| 9 | const resolved = new URL(returnTo, 'https://app.example.com'); |
| 10 | return resolved.origin === 'https://app.example.com' ? resolved.pathname + resolved.search : '/'; |
| 11 | } |
| 12 |
|
| 13 | export default function handler(req: NextApiRequest, res: NextApiResponse) { |
| 14 | res.redirect(safeReturnPath(req.query.returnTo)); |
| 15 | } |
Explanation (EN)
Only same-origin relative paths are accepted, with explicit guards for protocol-relative (//) and backslash bypasses, and a final origin check after normalization. The redirect can never leave your domain, which defeats both phishing and auth-token leakage.
Objašnjenje (HR)
Prihvaćaju se samo same-origin relativni pathovi, uz eksplicitne zaštite za protocol-relative (//) i backslash zaobilaženja, te završnu provjeru origina nakon normalizacije. Redirect nikad ne može napustiti vašu domenu, što poražava i phishing i curenje auth tokena.
Notes (EN)
Maps to OWASP A01 / Unvalidated Redirects and Forwards Cheat Sheet. The same rule applies to client-side router.push(userValue) and window.location = userValue.
Bilješke (HR)
Mapira se na OWASP A01 / Unvalidated Redirects and Forwards Cheat Sheet. Isto pravilo vrijedi za client-side router.push(userValue) i window.location = userValue.
Exceptions / Tradeoffs (EN)
If cross-domain redirects are a real requirement (e.g. SSO to known partners), validate the full target against an explicit allowlist of permitted external origins instead of allowing arbitrary hosts.
Iznimke / Tradeoffi (HR)
Ako su cross-domain redirecti stvarni zahtjev (npr. SSO prema poznatim partnerima), validirajte cijelo odredište prema eksplicitnom allowlistu dopuštenih vanjskih origina umjesto dopuštanja proizvoljnih hostova.