• 4 heures
  • Facile

La CSRF

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Explication

La faille CSRF ("Cross site request forgery") est très souvent assimilée à la XSS alors que ces deux failles sont diamétralement opposées. Quand la XSS cherche à dérober des informations personnelles de l'utilisateur, la CSRF cherche à lui faire exécuter des actions à son insu directement sur son ordinateur. Dans la première partie, on a vu comment le pirate utilisait la XSS pour voler les cookies de l'administrateur, pour pouvoir prendre le contrôle du site. Si le hacker décidait d'utiliser la CSRF, il ferait exécuter l'action directement sur l'ordinateur de la victime.

Un petit exemple pour clarifier tout ça :

Je suis membre d'un forum, est il y a une personne que je veux bannir du forum parce-que je ne l'aime pas. Je sais que pour supprimer un profile, l'administrateur se rend sur l'URL suivant

www.monsite.com/index.php?profile=mon_ennemi&action=supprimer

Évidemment le script qui se trouve derrière vérifie que la personne qui se rend sur l'URL est bien administrateur. Je dois donc trouver un moyen pour me rendre sur cet URL en étant administrateur. Deux choix s'offrent alors à moi. Soit j'utilise une faille XSS pour subtiliser les cookies de l'administrateur et me connecter ultérieurement, tranquillement de chez moi. Soit j'utilise un CSRF pour le rediriger vers cette page, et ainsi lui faire supprimer mon ennemi sans qu'il ne le sache.

Et cette faille est compatible avec n'importe quel site, pour peu que vous y soyer connecté. Si vous êtes connecté à votre compte Facebook, le hacker peut se servir d'un CSRF pour vous faire aimer une page, publier un message, etc... Et cela marche avec beaucoup de sites, pour peu que vous y soyez connecté et qu'il ne soit pas protégé. Si les banques n'étaient pas protégées contre ce type d'attaque, un hacker pourrait sans problème rédiger un script qui vous fait envoyer de l'argent à quelqu'un.

Comme vous le voyez, il est compliqué de se concentrer sur un seul exemple, étant donné que les applications de cette faille sont presque infinies. Et vous allez voir qu'il est également compliqué de s'en protéger complètement.

Comment s'en protéger

Authentification pat jeton (token)

Comme je l'ai dit précédemment, il n'existe malheureusement pas de protection parfaite contre la CSRF. La façon la plus rependue étant l'utilisation d'un jeton unique qui sera vérifié à chaque modification. Beaucoup d'internautes préconisent d'utiliser la fonction uniqid(), mais c'est en fait une erreur. Elle génère un identifiant unique basé sur le temps en microsecondes, et contrairement à ce que l'on pourrait penser, sa valeur n'est pas impossible à deviner. Il est donc, à mon gout, plus sécurisé d'utiliser ceci :

<?php $token = bin2hex(mcrypt_create_iv(32, MCRYPT_DEV_URANDOM)); ?>

D'après la documentation PHP :

mcrypt_create_iv() crée un IV (vecteur d'initialisation) à partir d'une source aléatoire.

Le vecteur d'initialisation est le seul moyen de fournir une initialisation de remplacement aux méthodes d'initialisation. Ce vecteur n'a pas besoin d'être particulièrement secret, même si c'est mieux. Vous pouvez l'envoyer avec vos documents chiffrés sans perdre en sécurité.

Un vecteur d'initialisation ? Mais quelle-est donc cette sorcellerie ?!!

Bonne question Jack ! Mais sache que l'on pourrait écrire un cours entier dédié à cette notion. Globalement, il faut simplement comprendre que c'est un bloc de bits très utile en cryptographie. Il a des propriétés très intéressante dans les domaines de la sécurité. Par exemple, une fois associé à une clé privée, on peut générer un grand nombre de clés dérivées différentes au fur et à mesure que IV change. Il permet également une rotation rapide de ces clés.

Pour être franc, dans notre cas on s'en tamponne royalement. J'utilise cette fonction pour générer un nombre (pseudo) aléatoire plus solide - selon moi - qu'en utilisant microtime(). C'était aussi l'occasion d'introduire la notion de vecteur d'initialisation, et de piquer votre curiosité :)

Revenons donc à nos moutons. Voilà un exemple de formulaire protégé par token :

<?php

// On démarre la session en début de chaque page
session_start();

//On enregistre notre token
$token = bin2hex(mcrypt_create_iv(32, MCRYPT_DEV_URANDOM));

$_SESSION['token'] = $token;

?>

<!DOCTYPE html>
<html>
  <head>
        <meta charset="utf-8"/>
<!--[if lt IE 9]>
    <script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
      <link rel="stylesheet" href="test.css" />
     <title>Mon Site</title>
  </head>
  <body>
    <form>
        <!-- Pseudo de la personne à supprimer -->
        <input type="text" name="pseudo" id="pseudo" />
        <input type="submit" value="valider" />
        <!-- Notre token de vérification, bien caché -->
        <input type="hidden" name="token" id="token" value="<?php echo $token; ?>" />
    </form>
  </body>
 </html>

Si on voulait bien faire les choses, on mettrait en place une page PHP qui se chargerait de générer le token s’il n'existe pas déjà. Voilà, on a plus qu'à vérifier lors du traitement de la demande :

<?php

session_start();

//On vérifie que tous les jetons sont là
if (isset($_SESSION['token']) AND isset($_POST['token']) AND !empty($_SESSION['token']) AND !empty($_POST['token'])) {

    // On vérifie que les deux correspondent
    if ($_SESSION['token'] == $_POST['token']) {

                // Vérification terminée
                // On peut supprimer l'utilisateur

        }
}

else {
    
    // Les token ne correspondent pas
    // On ne supprime pas

    echo "Erreur de vérification";
}

?>

Si on voulait faire encore mieux, on pourrait rajouter une variable de session qui enregistre l'heure de la création du token. On met ensuite en place un système qui vérifie que le token n'a pas été créé il y a trop longtemps. En général le délai d'expiration des token est de 10 minutes. Il est également possible d'utiliser les jetons hors formulaire en les faisant passer dans l'URL.

www.monsite.com/index.php?profile=mon_ennemi&action=supprimer&token22f2f68d45fe0baea8d064bdd4604391ba95752b4df6c85f478c56207addebb9

Bon voilà en gros ce type de protection devrai en décourager plus d'un, et on pourrait s'arrêter là. Mais il existe d'autres petites techniques qui complètent bien l'utilisation d'un token, et je serais bien idiot de ne pas vous en parler

Demande de confirmation

Bon pour cette technique pas besoin d'épiloguer. Il s'agit simplement de demander à l'administrateur de confirmer l'action avec un pop-up de confirmation ou même mieux, une confirmation par mot de passe. Ainsi, on réduit encore plus le risque de suppression involontaire.

Un petit captcha

Une autre technique consiste à demander à l'administrateur de valider l'action en remplissant un captcha. C'est tout bête et très efficace, mais pas très adapté si l'action est répétitive...

Si vous ne savez pas comment mettre en place un captcha, je vous renvoie à la fin de ce cours où tout est expliqué.

Vérifier le Referer Header (Controversé)

Cette protection est très connue et recommandée par de nombreux sites. Il est vrai qu'il est toujours bon d'ajouter une vérification complémentaire, mais vous devez savoir qu'il est possible de contourner cette vérification en modifiant le HTTP_REFERER.

Ainsi vous pouvez l'utiliser comme protection supplémentaire, mais n'ayez pas trop confiance en cette technique. Voilà malgré tout un petit code :

<?php

if($_SERVER['HTTP_REFERER'] == 'http://www.monsite.com/formulaire_suppression.php') {

 // On a vérifié la provenance de la requête
 // On passe à la suite

}

?>

Donc si on reprend notre formulaire de tout à l'heure :

<?php

session_start();

//On vérifie que tous les jetons sont là

if (isset($_SESSION['token']) AND isset($_POST['token']) AND !empty($_SESSION['token']) AND !empty($_POST['token']))

{

    // On vérifie que les deux correspondent
    if ($_SESSION['token'] == $_POST['token']) {

            // On vérifie que la requête vient bien du formulaire
            if ($_SERVER['HTTP_REFERER'] == 'http://www.monsite.com/formulaire_suppression.php') {

                // On a vérifié la provenance de la requête
                // On peut supprimer

            }

            // La requête vient d'autre part, on bloque
            else {
                echo "La requête ne provient pas du formulaire";
            }
        }
}

else {

    // Les token ne correspondent pas
    // On ne supprime pas

    echo "Erreur de vérification";

}

?>

Magnifique ! Même si ce n'est pas 100% sûr, ça rajoute une protection et on ne va pas cracher dessus. En plus ça prend vraiment pas beaucoup de place alors pourquoi s'en priver ?

Idées reçues sur la protection

Beaucoup de gens rivalisent d'ingéniosité pour contrer cette faille. Mais bien souvent, les petites bidouilles ne protègent rien du tout. Voilà ce qui, contrairement aux idées reçues, ne vous protègera pas :

  • Vérification par cookie ultra secret

  • Oublier la méthode GET et n'utiliser que POST

  • Ajouter plein d'étapes

  • Utiliser de l'URL rewriting

Exemple de certificat de réussite
Exemple de certificat de réussite