Pour le code de sécurité, veuillez ne pas générer vos jetons de cette façon: $token = md5(uniqid(rand(), TRUE));
Essayez ceci:
Générer un jeton CSRF
PHP 7
session_start();
if (empty($_SESSION['token'])) {
$_SESSION['token'] = bin2hex(random_bytes(32));
}
$token = $_SESSION['token'];
Sidenote: L'un des projets open source de mon employeur est une initiative de backport random_bytes()
et de random_int()
projets PHP 5. Il est sous licence MIT et disponible sur Github et Composer en tant que paragonie / random_compat .
PHP 5.3+ (ou avec 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'];
Vérification du jeton CSRF
Ne vous contentez pas d'utiliser ==
ou même d' ===
utiliser hash_equals()
(PHP 5.6+ uniquement, mais disponible pour les versions antérieures avec la bibliothèque 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
}
}
Aller plus loin avec les jetons par formulaire
Vous pouvez restreindre davantage les jetons afin qu'ils ne soient disponibles que pour un formulaire particulier en utilisant hash_hmac()
. HMAC est une fonction de hachage à clé particulière qui peut être utilisée en toute sécurité, même avec des fonctions de hachage plus faibles (par exemple MD5). Cependant, je recommande d'utiliser la famille de fonctions de hachage SHA-2 à la place.
Tout d'abord, générez un deuxième jeton à utiliser comme clé HMAC, puis utilisez une logique comme celle-ci pour le rendre:
<input type="hidden" name="token" value="<?php
echo hash_hmac('sha256', '/my_form.php', $_SESSION['second_token']);
?>" />
Et puis en utilisant une opération congruente lors de la vérification du jeton:
$calc = hash_hmac('sha256', '/my_form.php', $_SESSION['second_token']);
if (hash_equals($calc, $_POST['token'])) {
// Continue...
}
Les jetons générés pour un formulaire ne peuvent pas être réutilisés dans un autre contexte sans le savoir $_SESSION['second_token']
. Il est important que vous utilisiez un jeton distinct comme clé HMAC que celui que vous venez de déposer sur la page.
Bonus: approche hybride + intégration Twig
Quiconque utilise le moteur de création de modèles Twig peut bénéficier d'une double stratégie simplifiée en ajoutant ce filtre à son environnement 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']);
}
)
);
Avec cette fonction Twig, vous pouvez utiliser les deux jetons à usage général comme ceci:
<input type="hidden" name="token" value="{{ form_token() }}" />
Ou la variante verrouillée:
<input type="hidden" name="token" value="{{ form_token('/my_form.php') }}" />
Twig ne s'occupe que du rendu des modèles; vous devez toujours valider correctement les jetons. À mon avis, la stratégie Twig offre une plus grande flexibilité et simplicité, tout en conservant la possibilité d'une sécurité maximale.
Jetons CSRF à usage unique
Si vous avez une exigence de sécurité selon laquelle chaque jeton CSRF est autorisé à être utilisable exactement une fois, la stratégie la plus simple le régénère après chaque validation réussie. Cependant, cela invalidera chaque jeton précédent qui ne se marie pas bien avec les personnes qui parcourent plusieurs onglets à la fois.
Paragon Initiative Enterprises gère une bibliothèque Anti-CSRF pour ces cas d'angle. Il fonctionne avec des jetons à usage unique par formulaire, exclusivement. Lorsque suffisamment de jetons sont stockés dans les données de session (configuration par défaut: 65535), les jetons non utilisés les plus anciens sont d'abord supprimés.
token_time
ça sert?