eduardweb.
Securitate & AuthIntermediar#security#nextjs#typescript#owasp#code-review

OWASP în 2026: La ce mă uit în PR-uri pe un proiect de Next.js

De Elena Dumitrescu, 28 iul. 2026 · 9 vizualizări · 3 like-uri

Postat 28 iul. 2026
typescript
// ❌ GREȘIT: Se bazează doar pe tipurile TS și nu verifică sesiunea
export async function deleteUserBad(userId: string) {
  await db.user.delete({ where: { id: userId } });
}

// ✅ CORECT: Verificare auth pe server + validare runtime cu Zod
import { z } from 'zod';
import { auth } from '@/lib/auth';

const InputSchema = z.object({ userId: z.string().uuid() });

export async function deleteUserGood(rawInput: unknown) {
  const session = await auth();
  if (!session || session.user.role !== 'ADMIN') {
    throw new Error('Unauthorized');
  }

  const { userId } = InputSchema.parse(rawInput);
  await db.user.delete({ where: { id: userId } });
}

Săptămâna trecută am prins la PR review o chestie banală, dar periculoasă: o Server Action din Next.js 15 care ștergea un cont fără nicio verificare de sesiune pe backend. Juniorul presupusese că dacă butonul e ascuns în UI pentru userii simpli, acțiunea e deja securizată.

Server Actions au schimbat radical modul în care dezvoltăm în Next.js, dar au adus și un val de vulnerabilități stupide. De la arhitecturi vechi unde aveai un API Gateway cu middleware centralizat, am ajuns să expunem funcții Node.js direct în frontend printr-un simplu 'use server'. Haideți să vedem la ce mă uit eu la review pe o aplicație cu peste 20k de utilizatori activi.

1. Server Actions nu sunt funcții private

Cea mai mare greșeală pe care o văd săptămânal: colegii tratează Server Actions ca pe niște funcții interne dintr-un helper. În spate, Next.js generează un endpoint HTTP POST public. Oricine poate trimite un request cURL cu payload arbitrar către acel endpoint dacă îi ghicește sau intercepta id-ul acțiunii.

Când verific un PR, caut două lucruri obligatorii în orice fișier cu 'use server':

  • Authentication & Authorization: Există await auth() sau o verificare de rol chiar în prima linie a acțiunii?
  • Validation: Input-ul este trecut prin Zod? Niciodată nu am încredere în tipurile TypeScript trimise din client. Un atacator nu folosește formularul tău din React.

Trade-off-ul e evident: scrii mai mult boilerplate și codul devine puțin mai incărcat. Pierzi 30 de secunde să definești o schemă Zod, dar dormi liniștit noaptea.

2. SQL Injection în era Drizzle și Prisma

Majoritatea cred că dacă folosesc un ORM modern au scăpat complet de SQLi. Fals. Am avut cazul la un refactoring unde un coleg dorea o căutare mai rapidă și a trecut pe interogări raw.

Problematic este când vezi prisma.$queryRawUnsafe sau concatenări directe de string-uri în Drizzle (sql.raw(...)). Dacă un PR conține un template literal de JS într-o interogare SQL, pun direct Request Changes. Folosiți întotdeauna tagged template literals furnizate de ORM sau parametri positional ($1, $2) pentru a lăsa driverul de DB să facă escaping.

3. XSS-ul nu a murit, doar s-a mutat

React face auto-escaping implicit, dar App Router aduce noi capcane. Cea mai frecventă greșeală pe care o văd la integrările cu headless CMS-uri este folosirea de dangerouslySetInnerHTML fără sanitizare.

Dacă HTML-ul provine dintr-un rich-text editor unde userii pot introduce conținut, este obligatoriu să treci string-ul printr-o librărie gen isomorphic-dompurify înainte de afișare. Am economisit odată 2 zile de incident response doar pentru că am blocat un PR care randat comentarii nefiltrate.

4. Checklist-ul meu rapid la PR review

Când deschid un diff pe GitHub, caut direct după aceste 4 chestii:

  1. Funcții cu 'use server' fără verificări explicite de drepturi.
  2. Utilizări de dangerouslySetInnerHTML fără sanitizare.
  3. Interogări raw SQL care concatenează variabile din request.
  4. Cookie-uri de sesiune setate manual fără flag-urile HttpOnly, Secure și SameSite=Lax.

La voi în echipă cum gestionați securitatea pe Server Actions? Aveți reguli de ESLint scrise de voi sau vă bazați pe manual review?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

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