eduardweb.
Securitate & AuthIntermediar#nextjs#react#securitate#owasp

Securitate Next.js: La ce mă uit în PR-uri ca să nu ne spargă nimeni

De Alin Pătrașcu, 9 iun. 2026 · 1 vizualizări · 2 like-uri

Postat acum 7 ore
typescript
// Exemplu de Server Action sigur vs nesigur

// ❌ NESIGUR: Încredere oarbă în input și concatenare SQL
export async function updateBioUnsafe(userId: string, bio: string) {
  await db.execute(`UPDATE users SET bio = '${bio}' WHERE id = '${userId}'`);
}

//  SIGUR: Validare cu Zod și parametrizație
import { z } from 'z';

const UpdateBioSchema = z.object({
  userId: z.string().uuid(),
  bio: z.string().min(1).max(160),
});

export async function updateBioSafe(rawInput: unknown) {
  const parsed = UpdateBioSchema.safeParse(rawInput);
  if (!parsed.success) throw new Error('Input invalid');
  
  // Query-ul folosește parametri, prevenind SQLi
  await db.execute(
    'UPDATE users SET bio = ? WHERE id = ?',
    [parsed.data.bio, parsed.data.userId]
  );
}

Am făcut recent un audit de securitate pe o aplicație Next.js cu vreo 15.000 de utilizatori activi și am găsit niște vulnerabilități destul de evidente, deși echipa era convinsă că framework-ul rezolvă totul din oficiu. Trecerea masivă la Server Components și Server Actions a mutat dinamica atacurilor OWASP direct în codul nostru de zi cu zi. Framework-ul ne ajută, dar nu face minuni dacă scriem cod cu ochii închiși.

Iată la ce mă uit de fiecare dată când fac review pe un PR, ca să nu ne trezim cu baza de date ștearsă sau cu token-urile utilizatorilor scurse pe net.

Server Actions și noul SQL Injection

Toată lumea a crezut că ORM-urile moderne au ucis SQL injection-ul pentru totdeauna. Ei bine, am prins recent un coleg care, obosit fiind, a scris o interogare brută într-un Server Action folosind concatenare simplă de string-uri. Argumentul lui a fost clasic: „E pe server, nu are cum să injecteze nimic din browser”.

Asta este marea capcană în Next.js. Server Actions arată ca niște funcții simple de TypeScript pe care le apelezi din client, dar ele sunt, de fapt, endpoint-uri POST generate automat sub capotă. Orice parametru trimis acolo poate fi interceptat, modificat și trimis direct către baza ta de date de către un atacator care folosește un proxy simplu.

Dacă nu validezi input-ul direct la intrarea în funcție, ești expus complet. Eu nu las niciun PR să treacă în producție dacă Server Action-ul nu începe cu o schemă Zod care parsează argumentele. Este un trade-off acceptabil: scrii cu 5 linii de cod în plus, dar măcar dormi liniștit noaptea.

XSS-ul din Server Components

React ne protejează nativ de Cross-Site Scripting (XSS) prin escaparea automată a string-urilor în JSX. Totuși, mulți dezvoltatori încă folosesc dangerouslySetInnerHTML pentru a reda conținut din CMS-uri sau markdown introdus de utilizatori.

Dacă textul ăla vine dintr-o bază de date unde un utilizator rău intenționat a salvat un script, ai pierdut controlul aplicației. În PR review-uri, caut mereu acest atribut periculos. Dacă există, verific dacă input-ul este trecut printr-un utilitar de sanitizare serios, cum ar fi isomorphic-dompurify. Fără sanitizare pe server-side, PR-ul primește direct Reject.

De asemenea, mare atenție la tag-urile de tip link cu URL-uri dinamice. Dacă un utilizator își poate seta link-ul de profil ca javascript:alert(1), Next.js îl va randa fără probleme, iar la click se va executa scriptul în contextul aplicației tale.

CSRF-ul pe Route Handlers

Next.js vine cu protecție CSRF integrată pentru Server Actions, verificând automat header-ul Origin. Problema apare când colegii scriu Route Handlers custom în app/api/route.ts și uită că acolo protecția nu mai este aplicată automat.

Dacă folosești autentificare bazată pe cookie-uri și ai endpoint-uri care modifică date (POST, PUT, DELETE) fără verificare de token CSRF sau fără flag-ul SameSite=Strict pe cookie-uri, ești vulnerabil. La un proiect anterior, am economisit zeci de ore de incident response doar pentru că am impus SameSite Strict din prima zi.

Voi cum gestionați validarea în Server Actions? Folosiți middleware sau puneți Zod direct în fiecare funcție?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

Doar membrii comunității pot lăsa comentarii.