// ❌ VULNERABIL: ID-ul vine de pe client și interogarea e brută
export async function deleteCommentVulnerable(commentId: string, userId: string) {
await db.$executeRawUnsafe(`DELETE FROM Comment WHERE id = '${commentId}' AND userId = '${userId}'`);
}
// SECURIZAT: Validare Zod, sesiune pe server și query parametrizat
export async function deleteCommentSecure(commentId: string) {
const session = await auth();
if (!session?.user?.id) throw new Error("Neautorizat");
await db.comment.delete({
where: {
id: commentId,
userId: session.user.id
}
});
}Am făcut recent un audit pe un proiect Next.js cu vreo 12k utilizatori activi și mi s-a zbârlit părul pe ceafă când am văzut ce trecea de code review. Lumea are impresia că dacă folosește un framework modern și deployează pe Vercel, securitatea se rezolvă de la sine. Este complet fals.
În 2026, Next.js este extrem de matur, dar flexibilitatea lui vine cu un cost. Am văzut bube mari de OWASP Top 10 ascunse în Server Components și Server Actions. Dacă faci review pe un PR de Next.js, sunt câteva detalii specifice la care trebuie să fii extrem de atent ca să nu vă treziți cu datele pe Telegram.
1. Server Actions și noul val de IDOR / CSRF
Next.js are protecție CSRF integrată pentru Server Actions prin verificarea headerelor de origine. Super tare. Însă problema majoră pe care o văd zilnic este IDOR (Insecure Direct Object Reference).
Mulți juniori trimit userId ca parametru ascuns într-un formular sau direct în apelul funcției din client. Câștigi 2 minute la scris cod, dar trade-off-ul e catastrofal: oricine poate deschide DevTools, modifică ID-ul din request și șterge datele altui user.
Regula mea de aur la PR: niciodată nu ai încredere în ID-ul de user trimis de client. Identitatea userului se extrage direct pe server, în interiorul acțiunii, citind sesiunea securizată (de exemplu, prin Auth.js sau Clerk).
2. SQL Injection în era ORM-urilor
„Folosim Prisma/Drizzle, n-avem cum să luăm SQL injection”. Am auzit placa asta de zeci de ori. Totul merge bine până când apare o cerință de căutare complexă cu filtre dinamice.
Pentru că ORM-ul devine lent sau greu de scris pentru query-uri complexe, dev-ul cedează ispitei și folosește un query raw. Dacă în loc de query parametrizat folosește string interpolation cu backticks pentru că „se grăbea”, gata vulnerabilitatea. La un proiect de e-commerce am găsit un astfel de query raw care permitea dumping complet de tabelă prin simpla adăugare a unui apostrof în inputul de search.
3. XSS-ul nu a murit, doar s-a mutat în Server Components
React ne protejează de XSS prin escaparea automată a stringurilor. Dar ce te faci când aduci date dintr-un CMS extern sau dintr-un editor de text îmbogățit (rich text) folosit de utilizatori?
Proprietatea dangerouslySetInnerHTML este încă foarte folosită. Dacă vezi asta într-un PR fără un utilitar de sanitizare precum isomorphic-dompurify aplicat pe server înainte de randare, dă direct reject. Un simplu tag <img src=x onerror=alert(1)> salvat în baza de date va rula în browserul fiecărui vizitator al acelei pagini.
Checklist rapid pentru următorul tău PR Review
- Sesiunea pe server: Orice Server Action care modifică date apelează
auth()intern? Nu te baza doar pe middleware-ul global pentru rute, Server Actions pot fi apelate direct prin POST. - Validarea inputului: Toate argumentele care intră într-o acțiune de server sunt trecute printr-o schemă Zod? Dacă nu au validare de tip și lungime, refuză PR-ul.
- Variabile de mediu: Verifică să nu existe secrete de API în variabile cu prefixul
NEXT_PUBLIC_.
Voi ce folosiți pentru scanarea automată a vulnerabilităților în proiectele de Next.js, vă bazați pe tool-uri în CI/CD sau faceți threat modeling manual?