CSRF Token in PHP: guida completa e sicura
Le web application moderne devono proteggere le azioni degli utenti autenticati. Una delle vulnerabilità più diffuse è il Cross-Site Request Forgery (CSRF): un attacco in cui un sito malevolo induce il browser di un utente autenticato a inviare richieste non intenzionali verso un’applicazione legittima.
Il CSRF token è una delle difese più solide e universalmente accettate per prevenire questo tipo di attacco, soprattutto quando si gestiscono operazioni che modificano lo stato del sistema.
Cos’è un CSRF Token
Un CSRF token è una stringa crittograficamente sicura, generata dal server e associata alla sessione dell’utente. Il token viene inviato insieme a ogni richiesta sensibile e verificato dal server prima di accettare l’operazione.
Perché il CSRF è pericoloso
- Sfrutta l’autenticazione già valida dell’utente
- Non richiede il furto delle credenziali
- Può modificare dati, impostazioni o eseguire azioni critiche
Modello di funzionamento corretto
- Generazione del token lato server
- Inserimento del token nella richiesta
- Verifica lato server con confronto sicuro
- Scadenza e rotazione del token
Implementazione in PHP
Gestione sicura della sessione
<?php
session_start();
ini_set('session.cookie_httponly', '1');
ini_set('session.use_strict_mode', '1');
?>
Generazione e verifica del token
<?php
function csrf_generate(): string {
$token = bin2hex(random_bytes(32));
$_SESSION['csrf_token'] = $token;
$_SESSION['csrf_time'] = time();
return $token;
}
function csrf_verify(string $token, int $ttl = 900): bool {
if (!isset($_SESSION['csrf_token'], $_SESSION['csrf_time'])) {
return false;
}
if ((time() - $_SESSION['csrf_time']) > $ttl) {
return false;
}
return hash_equals($_SESSION['csrf_token'], $token);
}
?>
Inserimento nel form HTML
<input type="hidden" name="csrf_token"
value="<?= htmlspecialchars(csrf_generate(), ENT_QUOTES) ?>">
Verifica lato server
<?php
if (!csrf_verify($_POST['csrf_token'] ?? '')) {
http_response_code(400);
exit('Richiesta non valida');
}
?>
Best practice fondamentali
- Usare sempre HTTPS
- Token con scadenza temporale
- Confronto con hash_equals()
- Rigenerare il token dopo operazioni critiche
- Non affidarsi solo a SameSite cookie
CSRF e XSS
Il CSRF token non protegge da vulnerabilità XSS. Se uno script malevolo può essere eseguito nel browser dell’utente, il token può essere letto e riutilizzato. La protezione CSRF deve sempre essere accompagnata da una corretta sanitizzazione dell’output e da politiche CSP.
Conclusione
L’uso corretto dei CSRF token rappresenta uno standard minimo di sicurezza per qualsiasi applicazione PHP moderna. È una difesa semplice, efficace e con un costo implementativo minimo rispetto ai danni potenziali che previene.
Esempio completo: form + verifica CSRF
Di seguito un esempio minimale ma completo che mostra l’intero flusso di protezione CSRF in PHP: generazione del token, inserimento nel form e verifica lato server.
index.php – Pagina con il form
<?php
session_start();
ini_set('session.cookie_httponly', '1');
ini_set('session.use_strict_mode', '1');
function csrf_generate(): string {
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
$_SESSION['csrf_time'] = time();
}
return $_SESSION['csrf_token'];
}
?>
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<title>Form protetto da CSRF</title>
</head>
<body>
<form method="post" action="process.php">
<input type="hidden" name="csrf_token"
value="<?= htmlspecialchars(csrf_generate(), ENT_QUOTES, 'UTF-8') ?>">
<label>Nome</label>
<input type="text" name="name" required>
<button type="submit">Invia</button>
</form>
</body>
</html>
process.php – Verifica del token
<?php
session_start();
ini_set('session.cookie_httponly', '1');
ini_set('session.use_strict_mode', '1');
function csrf_verify(string $token, int $ttl = 900): bool {
if (!isset($_SESSION['csrf_token'], $_SESSION['csrf_time'])) {
return false;
}
if ((time() - $_SESSION['csrf_time']) > $ttl) {
return false;
}
return hash_equals($_SESSION['csrf_token'], $token);
}
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
exit('Metodo non consentito');
}
if (!csrf_verify($_POST['csrf_token'] ?? '')) {
http_response_code(400);
exit('Richiesta non valida');
}
// Token valido: qui puoi processare i dati
echo 'Form inviato correttamente';
// Rotazione del token dopo l’uso
unset($_SESSION['csrf_token'], $_SESSION['csrf_time']);
Questo schema è sufficiente per la maggior parte delle applicazioni PHP classiche e rappresenta una base solida da estendere per casi più complessi (AJAX, più form, token per azione).