Per il codice di sicurezza, non generare i token in questo modo: $token = md5(uniqid(rand(), TRUE));
Prova questo:
Generazione di un token CSRF
PHP 7
session_start();
if (empty($_SESSION['token'])) {
$_SESSION['token'] = bin2hex(random_bytes(32));
}
$token = $_SESSION['token'];
Nota a margine: uno dei progetti open source del mio datore di lavoro è un'iniziativa di backport random_bytes()
e random_int()
in progetti PHP 5. È concesso in licenza dal MIT e disponibile su Github e Composer come paragonie / random_compat .
PHP 5.3+ (o con ext-mcrypt)
session_start();
if (empty($_SESSION['token'])) {
if (function_exists('mcrypt_create_iv')) {
$_SESSION['token'] = bin2hex(mcrypt_create_iv(32, MCRYPT_DEV_URANDOM));
} else {
$_SESSION['token'] = bin2hex(openssl_random_pseudo_bytes(32));
}
}
$token = $_SESSION['token'];
Verifica del token CSRF
Non limitarti a usare ==
o anche a ===
usare hash_equals()
(solo PHP 5.6+, ma disponibile per le versioni precedenti con la libreria hash-compat ).
if (!empty($_POST['token'])) {
if (hash_equals($_SESSION['token'], $_POST['token'])) {
// Proceed to process the form data
} else {
// Log this as a warning and keep an eye on these attempts
}
}
Andare oltre con i token Per-Form
Puoi limitare ulteriormente i token in modo che siano disponibili solo per un particolare modulo utilizzando hash_hmac()
. HMAC è una particolare funzione hash con chiave che è sicura da usare, anche con funzioni hash più deboli (ad esempio MD5). Tuttavia, consiglio di utilizzare invece la famiglia di funzioni hash SHA-2.
Innanzitutto, genera un secondo token da utilizzare come chiave HMAC, quindi usa la logica come questa per renderlo:
<input type="hidden" name="token" value="<?php
echo hash_hmac('sha256', '/my_form.php', $_SESSION['second_token']);
?>" />
E quindi utilizzando un'operazione congruente durante la verifica del token:
$calc = hash_hmac('sha256', '/my_form.php', $_SESSION['second_token']);
if (hash_equals($calc, $_POST['token'])) {
// Continue...
}
I token generati per un modulo non possono essere riutilizzati in un altro contesto senza saperlo $_SESSION['second_token']
. È importante utilizzare un token separato come chiave HMAC rispetto a quello appena rilasciato nella pagina.
Bonus: approccio ibrido + integrazione ramoscello
Chiunque utilizzi il motore di modelli Twig può beneficiare di una doppia strategia semplificata aggiungendo questo filtro al proprio ambiente Twig:
$twigEnv->addFunction(
new \Twig_SimpleFunction(
'form_token',
function($lock_to = null) {
if (empty($_SESSION['token'])) {
$_SESSION['token'] = bin2hex(random_bytes(32));
}
if (empty($_SESSION['token2'])) {
$_SESSION['token2'] = random_bytes(32);
}
if (empty($lock_to)) {
return $_SESSION['token'];
}
return hash_hmac('sha256', $lock_to, $_SESSION['token2']);
}
)
);
Con questa funzione Twig, puoi utilizzare entrambi i token generici in questo modo:
<input type="hidden" name="token" value="{{ form_token() }}" />
O la variante bloccata:
<input type="hidden" name="token" value="{{ form_token('/my_form.php') }}" />
Twig si occupa solo del rendering dei modelli; è comunque necessario convalidare correttamente i token. A mio parere, la strategia Twig offre maggiore flessibilità e semplicità, pur mantenendo la possibilità per la massima sicurezza.
Token CSRF monouso
Se hai un requisito di sicurezza per cui ogni token CSRF può essere utilizzato esattamente una volta, la strategia più semplice lo rigenera dopo ogni convalida riuscita. Tuttavia, ciò invaliderà ogni token precedente che non si mescola bene con le persone che navigano su più schede contemporaneamente.
Paragon Initiative Enterprises mantiene una libreria Anti-CSRF per questi casi d'angolo. Funziona esclusivamente con gettoni per modulo monouso. Quando è memorizzato un numero sufficiente di token nei dati della sessione (configurazione predefinita: 65535), verranno eliminati per primi i token non riscattati più vecchi.
token_time
serve?