Un browser manda i tuoi cookie con qualsiasi richiesta al tuo dominio, chiunque l'abbia provocata. Perciò una pagina su un altro sito può contenere un modulo che invia al tuo, e se il tuo visitatore ha la sessione aperta la richiesta arriva autenticata — e nei tuoi registri sembra del tutto legittima.
Cosa può fare
- Cambiare un indirizzo di posta e poi chiedere il ripristino della password verso quello nuovo.
- Fare un ordine, trasferire qualcosa, cancellare qualcosa.
- Qualsiasi cosa il tuo sito faccia con una POST e una sessione.
Il gettone
Un valore casuale conservato nella sessione e ripetuto nel modulo. Una pagina su un altro sito non può leggere la tua sessione, e quindi non può conoscere quel valore.
// when rendering the form
$_SESSION["csrf"] = $_SESSION["csrf"] ?? bin2hex(random_bytes(32));
?>
<input type="hidden" name="csrf" value="<?= htmlspecialchars($_SESSION['csrf']) ?>">
// when handling it
if (!hash_equals($_SESSION["csrf"] ?? "", $_POST["csrf"] ?? "")) {
http_response_code(400); exit;
}
Confronta con hash_equals, non con ==. Un confronto normale si ferma al primo carattere diverso, e il tempo che impiega lascia trapelare quanta parte del gettone era giusta.
E l'attributo del cookie
session.cookie_samesite = Lax
SameSite=Lax dice al browser di non mandare affatto il cookie su una POST fra siti diversi, e così ferma questa classe di attacchi prima che il tuo codice la veda. È un secondo strato, non un sostituto: protegge solo dove il browser è recente e si comporta bene.
Una GET non deve cambiare nulla
Un collegamento che cancella qualcosa è un CSRF che aspetta solo di accadere — e peggio, un crawler o un'anteprima di collegamento lo faranno scattare da soli. Tutto ciò che cambia lo stato è una POST con gettone.
Qualsiasi framework lo porta già dentro e acceso. Se l'hai spento su un punto di accesso per far funzionare qualcosa, è proprio quel punto da guardare.