Cross‑Site Request Forgery (CSRF) rimane uno dei vettori di attacco più sfruttati nelle applicazioni web. Sebbene framework come Next.js offrano numerosi meccanismi per facilitare la costruzione di interfacce sicure, gli sviluppatori spesso trascurano le difese di base. In questo articolo mostreremo, passo dopo passo, come proteggere un'applicazione Next.js da CSRF, combinando i cookie SameSite e il pattern double‑submit token. Discuteremo il modello di minaccia, la meccanica di funzionamento, esempi di codice, checklist di mitigazione e le trappole più comuni.
Modello di minaccia – come avviene un attacco CSRF
L'attacco CSRF sfrutta il contesto autorizzato del browser della vittima. L'utente è autenticato nell'applicazione e poi visita un sito maligno che invia una richiesta HTTP a un endpoint protetto (es. POST /api/transfer). Il browser allega automaticamente i cookie di sessione, facendo credere al server che la richiesta sia legittima. Senza un meccanismo di verifica aggiuntivo, il server non distingue tale richiesta da un'azione voluta dall'utente.
Perché i cookie SameSite non bastano da soli
Impostare il flag SameSite=Lax o SameSite=Strict sul cookie di sessione limita l'invio automatico in contesti cross‑site. Tuttavia, nella pratica esistono scenari in cui i browser consentono l'invio del cookie (es. richieste GET in modalità Lax o browser più vecchi non supportati). Perciò è consigliato combinare SameSite con un token aggiuntivo verificato sul server.
Double‑submit token – principio di funzionamento
Il pattern double‑submit token consiste nel generare un token casuale sul server e inviarlo sia nel cookie sia nell'intestazione (o nel corpo) della richiesta. Il server, al ricevimento, confronta i due valori – se coincidono, la richiesta è considerata autentica. Il token non richiede memorizzazione sul server (stateless), semplificando il scaling.
„Il token doppio è il modo più semplice per introdurre una verifica CSRF robusta senza introdurre sessioni sul server.”
Implementazione in Next.js – parte server
Next.js espone API Routes che funzionano come funzioni server. Di seguito trovi un frammento di codice che genera il token, lo salva in un cookie SameSite=Lax e lo verifica nelle richieste successive.
import { NextApiRequest, NextApiResponse } from 'next';
import crypto from 'crypto';
const CSRF_COOKIE = 'csrfToken';
const CSRF_HEADER = 'x-csrf-token';
function generateToken() {
return crypto.randomBytes(32).toString('hex');
}
export default function handler(req: NextApiRequest, res: NextApiResponse) {
// 1️⃣ Generazione token su GET (es. al rendering del form)
if (req.method === 'GET') {
const token = generateToken();
res.setHeader('Set-Cookie', `${CSRF_COOKIE}=${token}; HttpOnly; SameSite=Lax; Path=/; Secure`);
return res.status(200).json({ csrfToken: token });
}
// 2️⃣ Verifica su POST/PUT/DELETE
const cookieToken = req.cookies[CSRF_COOKIE];
const headerToken = req.headers[CSRF_HEADER];
if (!cookieToken || !headerToken || cookieToken !== headerToken) {
return res.status(403).json({ error: 'Invalid CSRF token' });
}
// … logica aggiuntiva dell'endpoint
return res.status(200).json({ success: true });
}
Nel esempio sopra il token è generato al primo GET e restituito sia nel cookie sia nella risposta JSON. Il client (es. un componente React) deve leggere il token e includerlo nell'intestazione x-csrf-token nelle richieste mutanti successive.
Implementazione in Next.js – parte client (React)
Nei componenti React possiamo usare l'hook useEffect per recuperare il token e impostare l'intestazione di default nella libreria axios o fetch. Ecco un esempio con fetch:
import { useEffect } from 'react';
export default function useCsrf() {
useEffect(() => {
fetch('/api/csrf')
.then(r => r.json())
.then(data => {
// token disponibile in data.csrfToken e nel cookie
window.csrfToken = data.csrfToken;
});
}, []);
}
export async function csrfFetch(url, options = {}) {
const headers = {
...options.headers,
'x-csrf-token': window.csrfToken,
};
return fetch(url, { ...options, headers, credentials: 'include' });
}
L'impostazione credentials: 'include' garantisce che il cookie con il token venga inviato con la richiesta, mentre l'intestazione x-csrf-token consente la verifica doppia.
Checklist di mitigazione CSRF in Next.js
- Imposta il flag
SameSite=Lax(oStrictse l'app non richiede POST cross‑site) eSecurein tutti i cookie di sessione. - Genera un token unico per sessione e conservalo in un cookie http‑only.
- Invia il token in un'intestazione dedicata (
x-csrf-token) in tutte le richieste mutanti. - Verifica la corrispondenza tra token cookie e intestazione sul server prima di eseguire la logica di business.
- Limita la durata del token (es. 30 min) e rinnovalo ad ogni nuova sessione.
- Assicurati che tutti gli endpoint API che accettano dati (POST, PUT, DELETE, PATCH) abbiano abilitata la verifica CSRF.
Errori comuni e compromessi
Gli sviluppatori spesso commettono i seguenti errori:
- Mancanza del flag
SameSite– nei browser più vecchi il token può essere sfruttato, indebolendo la protezione. - Uso di un cookie non httpOnly – permette a script maligni di leggere il token, annullando il vantaggio del double‑submit.
- Memorizzazione del token in local storage – lo espone al furto tramite XSS.
- Verifica del token solo lato client – non fornisce protezione reale, poiché l'attaccante può bypassare il codice JavaScript.
- Impostare
SameSite=Strictin un'app che richiede link esterni – può causare il rifiuto inatteso di richieste legittime.
In pratica, la combinazione di SameSite e double‑submit token, insieme al principio del least‑privilege, è la strategia più sicura: limitare i permessi dei cookie e dei token restringendo il loro ambito ai percorsi necessari (Path=/api).
Riferimenti a standard e best practice
Le tecniche descritte sono conformi alle linee guida OWASP Top 10 – A05:2021 – Security Misconfiguration e CWE‑352 (Cross‑Site Request Forgery). Inoltre, l'uso del flag SameSite e del principio least‑privilege è raccomandato nel Progetto OWASP Secure Headers. L'implementazione double‑submit token soddisfa il requisito OWASP ASVS 2.2.1 (CSRF Prevention).
Esempio pratico – codice API completo e hook React
Di seguito trovi un esempio completo, pronto da copiare, che può essere collocato in /pages/api/csrf.ts e nel tuo hook /hooks/useCsrf.ts. In questo modo avrai sia l'endpoint che genera il token sia una funzione semplice per eseguire richieste protette.
// /pages/api/csrf.ts
import { NextApiRequest, NextApiResponse } from 'next';
import crypto from 'crypto';
const COOKIE_NAME = 'csrfToken';
const HEADER_NAME = 'x-csrf-token';
function newToken() {
return crypto.randomBytes(24).toString('base64');
}
export default function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method === 'GET') {
const token = newToken();
res.setHeader('Set-Cookie', `${COOKIE_NAME}=${token}; HttpOnly; SameSite=Lax; Secure; Path=/; Max-Age=1800`);
return res.status(200).json({ csrfToken: token });
}
const cookieToken = req.cookies[COOKIE_NAME];
const headerToken = req.headers[HEADER_NAME];
if (!cookieToken || !headerToken || cookieToken !== headerToken) {
return res.status(403).json({ error: 'Invalid CSRF token' });
}
// qui la logica dell'endpoint protetto
return res.status(200).json({ message: 'Protected action succeeded' });
}
// /hooks/useCsrf.ts
import { useEffect } from 'react';
export function useCsrf() {
useEffect(() => {
fetch('/api/csrf', { credentials: 'include' })
.then(r => r.json())
.then(data => {
(window as any).csrfToken = data.csrfToken;
});
}, []);
}
export async function csrfFetch(input: RequestInfo, init: RequestInit = {}) {
const token = (window as any).csrfToken;
const headers = new Headers(init.headers);
headers.set('x-csrf-token', token);
return fetch(input, { ...init, headers, credentials: 'include' });
}
Dopo aver importato useCsrf in un componente, il token verrà recuperato al montaggio e ogni chiamata a csrfFetch aggiungerà automaticamente l'intestazione richiesta.
Riepilogo e prossimi passi
La protezione da CSRF nelle applicazioni Next.js non richiede soluzioni complesse – basta combinare cookie SameSite, flag Secure e il pattern double‑submit token. Seguendo le linee guida OWASP, CWE e il principio del least‑privilege, fornisci una barriera solida contro richieste non autorizzate. Se necessiti di un audit di sicurezza, assistenza nell'integrazione o vuoi elevare il livello di protezione dell'intera piattaforma, contatta Coderia.it – costruiremo insieme un'app resistente alle minacce più recenti.



