Sari la conținut
← Bug-ul săptămânii

Bug-ul săptămânii · 19 octombrie 2026

Verificarea de redirect care are încredere în slash-ul de la început

Redirect-ul de după login într-un API Express · Securitate · dificultate 1/3

După un login reușit, API-ul îl trimite pe utilizator înapoi la pagina pe care o ceruse inițial. Destinația vine dintr-un parametru de query ?next=, deci e controlată de utilizator. Un coleg i-a cerut asistentului AI: „Scrie resolveRedirect(target, fallback) pentru endpoint-ul nostru de login. Permite redirect doar spre pagini de pe site-ul nostru; orice altceva trebuie să meargă la fallback.”

Codul a venit cu teste care trec. Contractul promis de docstring:

  • resolveRedirect(target, fallback) returnează target când e o cale locală de pe acest site și fallback în rest.
  • Un target lipsă, gol sau format doar din whitespace duce la fallback.
  • URL-urile absolute spre alte hosturi (https://evil.example) nu trebuie returnate niciodată.
  • isLocalPath(url) este predicatul din spate: true doar pentru căile pe care browserul le va rezolva pe originea curentă.

Ține minte cum interpretează un browser un header Location: nu caută doar http://. Fă review codului înainte să ajungă în producție.

Fără cont. Fără indicii. Timpul curge.0 s

Dă clic pe linia (sau liniile) cu defectul, alege categoria, apoi trimite. Cu tastatura: săgețile ca să te miști, Space ca să marchezi. În acest mod codul nu poate fi editat.

1/**
2 * Resolves the post-login redirect target.
3 *
4 * Returns `target` when it points to a page on this site, otherwise
5 * `fallback`. Used by the login endpoint to honour `?next=`.
6 */
7export function resolveRedirect(
8 target: string | null | undefined,
9 fallback = "/dashboard",
10): string {
11 if (!target) return fallback;
12 const trimmed = target.trim();
13 if (trimmed.length === 0) return fallback;
14 if (!isLocalPath(trimmed)) return fallback;
15 return trimmed;
16}
17
18/**
19 * True when `url` is a path on the current origin, not a link to another host.
20 */
21export function isLocalPath(url: string): boolean {
22 const lower = url.toLowerCase();
23 if (lower.startsWith("http://") || lower.startsWith("https://")) return false;
24 if (lower.includes("://")) return false;
25 return url.startsWith("/");
26}
0 linii marcate