// unsafe-vs-safe-action.ts
import { db } from "@/db";
import { z } from "zod";
// ❌ DE EVITAT: Fără verificare sesiune, input nevalidat, SQL vulnerabil
export async function unsafeUpdateBio(formData: FormData) {
const bio = formData.get("bio") as string;
const userId = formData.get("userId") as string;
await db.execute(`UPDATE users SET bio = '${bio}' WHERE id = ${userId}`);
}
// SIGUR: Validare Zod, verificare sesiune pe server, query parametrizat
const bioSchema = z.object({ bio: z.string().max(500) });
export async function safeUpdateBio(formData: FormData) {
const session = await getSession();
if (!session?.userId) throw new Error("Neautorizat");
const { bio } = bioSchema.parse({
bio: formData.get("bio")
});
await db.execute(
sql`UPDATE users SET bio = ${bio} WHERE id = ${session.userId}`
);
}Dacă faci review la PR-uri de Next.js ca și cum ai lucra pe un SPA clasic din 2018, ai o mare problemă. Cu Server Components și Server Actions, granița dintre client și server a dispărut aproape complet, iar asta deschide niște vectori de atac destul de urâți dacă echipa scrie cod din inerție.
Anul trecut, la un proiect cu vreo 12.000 de useri activi, era să ne luăm o mare gaură de securitate din cauza unui Server Action scris pe genunchi. De atunci, am implementat un set de reguli stricte la review.
CSRF-ul în epoca Server Actions
Multă lume crede că Next.js rezolvă CSRF implicit. Da, Next.js protejează Server Actions prin verificarea headerului Origin. Dar protecția asta funcționează doar dacă nu ai configurat CORS aiurea și dacă folosești request-uri standard de tip POST.
Am pățit ca un coleg să refolosească un Server Action într-un endpoint custom de API (/api/v1/update-profile) expus pentru o aplicație mobilă secundară. Din lene, a dezactivat verificarea de origin pentru acea rută. Rezultatul? Am lăsat ușa deschisă pentru atacuri cross-site pe acea acțiune specifică.
Când te uiți în PR, regula e simplă: dacă vezi rute de API clasice în loc de Server Actions, verifică manual dacă există validare de CSRF token sau dacă sunt protejate corect prin politici de SameSite pe cookie-uri.
SQL Injection în era ORM-urilor
"Nu mai scrie nimeni SQL brut în 2026, folosesc Prisma/Drizzle, deci sunt în siguranță." Asta e cea mai mare capcană.
Când ai de făcut un query complex pentru rapoarte financiare sau căutări text avansate, ORM-ul devine prea lent sau limitat. Developerii trec imediat pe raw SQL. Am salvat proiectul de la un dezastru când am văzut într-un PR un query construit prin interpolare de string-uri în loc de query parametrizat.
Trade-off-ul e evident: ORM-ul e bun pentru 90% din cazuri, dar când cobori la metal, trebuie să fii de zece ori mai atent. Dacă vezi concatenări de string-uri în query-uri, dă decline la PR instant.
XSS și hidratarea componentelor
În React Server Components (RSC), codul se randează pe server, deci ai crede că XSS-ul e mort. Nu e. Dacă aduci date din baza de date (de exemplu, comentarii scrise de useri) și le randezi folosind dangerouslySetInnerHTML fără să le treci printr-un parser de igienizare pe server, ai adus XSS-ul înapoi în joc.
Ce e și mai periculos în Next.js este hidratarea. Dacă serverul randează ceva, iar clientul primește altceva din cauza unui payload malițios care păcălește procesul de reconciliere, poți injecta scripturi direct în faza de hidratare.
La review, caută orice utilizare de dangerouslySetInnerHTML sau atribute custom transmise din baza de date direct în taguri HTML. Folosiți isomorphic-dompurify înainte ca datele nestructurate să atingă componenta.
Cum gestionați voi review-urile de securitate pe proiectele de Next.js? Vă bazați doar pe lintere sau faceți și threat modeling manual pe Server Actions?