web-dev-qa-db-fra.com

Comment ajouter correctement un jeton CSRF en utilisant PHP

J'essaie de sécuriser les formulaires sur mon site Web. L'un des formulaires utilise AJAX et l'autre est un formulaire simple "contactez-nous". J'essaie d'ajouter un jeton CSRF. Le problème que j'ai, c'est que le jeton ne fait que montrer up dans la "valeur" HTML de temps en temps. Le reste du temps, la valeur est vide. Voici le code que j'utilise sur la forme AJAX:

PHP:

if (!isset($_SESSION)) {
    session_start();
$_SESSION['formStarted'] = true;
}
if (!isset($_SESSION['token']))
{$token = md5(uniqid(Rand(), TRUE));
$_SESSION['token'] = $token;

}

HTML

 <form>
//...
<input type="hidden" name="token" value="<?php echo $token; ?>" />
//...
</form>

Aucune suggestion?

74
Ken

Pour le code de sécurité, ne générez pas 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'];

Note: L'un des projets open source de mon employeur est une initiative permettant de sauvegarder random_bytes() et random_int() dans PHP 5 projets. C'est MIT sous licence et disponible sur Github et Composer sous forme de 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 ===, Utilisez hash_equals() (PHP 5.6+ uniquement, mais disponible dans les versions antérieures avec le - hash-compat bibliothèque).

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 pour qu’ils ne soient disponibles que pour un formulaire particulier en utilisant hash_hmac() . HMAC est une fonction de hachage spécifique pouvant être utilisée en toute sécurité, même avec des fonctions de hachage plus faibles (par exemple, MD5). Cependant, je recommande d'utiliser plutôt la famille de fonctions de hachage SHA-2.

Commencez par générer 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 ensuite, 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 connaître $_SESSION['second_token']. Il est important d'utiliser un jeton distinct en tant que clé HMAC par rapport à celui que vous venez de déposer sur la page.

Bonus: approche hybride + Twig Intégration

Quiconque utilise le moteur de modélisation Twig peut bénéficier d’une stratégie simplifiée double en ajoutant ce filtre à leur 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 d'usage général comme suit:

<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'intéresse qu'au rendu des modèles; vous devez toujours valider les jetons correctement. À mon avis, la stratégie Twig) offre davantage de souplesse et de simplicité, tout en maintenant la possibilité d’une sécurité maximale.


Jetons CSRF à usage unique

Si vous avez des exigences de sécurité voulant que chaque jeton CSRF puisse être utilisé une seule fois, la stratégie la plus simple permet de le régénérer après chaque validation réussie. Cependant, cela invaliderait tous les jetons précédents qui ne se mêlaient pas bien aux personnes qui parcourent plusieurs onglets à la fois.

Paragon Initiative Enterprises gère un bibliothèque anti-CSRF pour ces cas de coin. Cela fonctionne avec les 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 échangés les plus anciens sont d'abord supprimés.

242
Scott Arciszewski

Avertissement de sécurité : md5(uniqid(Rand(), TRUE)) n'est pas un moyen sécurisé de générer des nombres aléatoires. Voir cette réponse pour plus d'informations et une solution utilisant un générateur de nombres aléatoires cryptographiquement sécurisé.

On dirait que vous avez besoin d'un autre avec votre si.

if (!isset($_SESSION['token'])) {
    $token = md5(uniqid(Rand(), TRUE));
    $_SESSION['token'] = $token;
    $_SESSION['token_time'] = time();
}
else
{
    $token = $_SESSION['token'];
}
23
datasage

La variable $token n'est pas récupéré de la session lorsqu'il est là

1
Dani