Les attaques CSRF

Auteur : palkeo
Créé le : le 06/09/2008 à 15:42
Visualisations : 3456
Ce tutoriel est composé des parties suivantes :

Introduction

Dans ce tutoriel, nous allons voir une faille très peu connue, qui peut pourtant être très dangereuse, si bien utilisée.
J'ai nommé : la faille CSRF.

La faille CSRF, c'est quoi ?

Prenons l'exemple d'un site ayant un livre d'or :
La page livredor.php permet d'afficher les messages du livre d'or, et d'en poster.
Il est possible d'utiliser du "code" pour mettre le texte en forme, insérer des liens, images...

Pour permettre aux administrateurs supprimer des messages du livre d'or, il y a une page del-message.php.
La page del-message.php prend pour argument l'ID du message à supprimer.
Par exemple, pour supprimer le message ayant pour id 5, l'administrateur ira sur del-message.php?id=5

Bien entendu, le livre d'or est protégé contre la faille XSS, et il faut être administrateur pour pouvoir supprimer des messages du livre d'or.

Jusque là, je suppose que vous vous dites que tout va bien, et qu'il n'y a pas de problèmes de sécurité.
Et bien c'est faux, ce site est vulnérable aux attaques CSRF !
Si un utilisateur utilise le code du site pour poster un message contenant une image ayant pour URL : del-message.php?id=5 tous les navigateurs affichant la page vont ouvrir del-message.php?id=5.
Ensuite, del-message.php va voir que les utilisateurs demandant la suppression du message ne sont pas administrateurs, et ne supprimera pas le message.
Puis, le navigateur, qui s'attend à recevoir une image, va obtenir un message d'erreur ("Erreur : vous n'êtes pas admin !"), et n'affichera donc pas l'image.
Mais maintenant, si l'administrateur affiche le message malveillant : le navigateur va demander del-message.php?id=5, et enverra aussi les cookies liés au site.
Le script del-message.php verra que c'est bien un administrateur, et supprimera le message du livre d'or :(
Le tout, sans action de la part de l'administrateur, à part l'affichage du livre d'or !

Donc, cette faille peut s'avérer très dangereuse, mais ça n'est pas tout :
Si un pirate s'arrange pour envoyer l'administrateur du site web sur une page spécialement crée pour l'occasion, il pourra aussi mettre, par exemple, une image qui pointera sur http://www.site-de-l-administrateur.com/del-message.php?id=5 ce qui aura exactement le même effet que la fois précédente, excepté le fait que le site peut avoir les meilleures protections du monde, ou encore interdire les images, ça ne changera rien, puisque l'attaque peut venir de n'importe quel site.

Exploitation

Pour exploiter cette faille, le plus efficace reste de faire pointer des images sur une page, qui, avec des paramètres spécifiques, fera une action, comme supprimer un message (si c'est un administrateur qui affiche la page), se déconnecter (pour n'importe quel membre)... Comme nous l'avons vu.

Vous noterez qu'il existe une technique de "triche" qui consiste à placer des images pointant vers des liens de parrainage pour gagner des points. Ces images peuvent même être placées sur des forums (dans sa signature, par exemple).
Toutefois, ne vous étonnez pas de vous faire bannir si le pot-aux-roses est découvert !

Il est aussi possible de faire là même chose en demandant de cliquer sur un simple lien, etc...
Mais il reste un problème : Avec cette technique, il est seulement possible d'envoyer des données GET, et on ne peut pas, par exemple, poster des messages, qui eux, sont la plupart du temps envoyés avec POST.

Toutefois, il est possible d'utiliser Javascript, pour envoyer des requêtes POST, et donc parvenir à nos fins !
Si vous voulez un exemple de script capable de faire ça, regardez cet exemple.
Heureusement (ou hélas, ça dépend de quel point de vue on prend la chose :o)) les développeurs des navigateurs y ont pensés, et la plupart des navigateurs bloquent les requêtes Ajax qui ne sont pas destinées au même site web que celui sur lequel il est.

Protection

Bien entendu, il est possible de se protéger :)

Toutefois, il n'existe pas de "solution miracle", mais il est possible de se défendre au mieux.

Voici trois moyens de défense efficaces (bien sur, il est possible de les combiner pour plus de sécurité) :

Moyen 1 : Éviter les GET
Cette technique est très simple, et consiste simplement à éviter au maximum d'utiliser GET pour les actions "critiques".
Comme nous l'avons vu, il est plus sécurisé d'utiliser POST.
Je déconseille d'utiliser seulement cette technique, mais pensez juste à utiliser POST si vous en avez la possibilité.

Moyen 2 : Vérifier le referer
Le referer est une chaîne de caractère qui contient l'URL que le navigateur a visité avant d'aller sur la page demandée.
En PHP, il est contenu dans la variable $_SERVER['HTTP_REFERER']
Ainsi, il suffit de vérifier que le referer contient bien la page depuis laquelle on est censé arriver, et effectuer la requête uniquement si le visiteur vient de la page en question.
Certains navigateurs, firewall, ou encore proxies peuvent automatiquement retirer le referer des requêtes, question de confidentialité. Donc, même si cette technique fonctionne avec 95% des utilisateurs, elle peut en bloquer d'autres.


Moyen 3 : Confirmation du mot de passe
Pour les actions critiques, une méthode simple et très sécurisée consiste simplement à demander à l'utilisateur son mot de passe.
Ainsi, il devient impossible d'effectuer ce type d'attaque, puisqu'il faut alors connaître le mot de passe de la victime...

Moyen 4 : Les jetons
Cette technique est un peu plus compliquée que les autres, mais c'est la plus puissante et sécurisée.
Il suffit de mettre dans chaque formulaire un champ masqué, qui contient une chaîne de caractères générée par PHP : Un jeton (ou token, en anglais).
Cette chaîne de caractère sera stockée dans un cookie, ou une variable de session... Puis, lors d'une action "importante", votre script devra vérifier que le jeton envoyé correspond bien à celui stocké dans la session ou le cookie.
Ainsi, si quelqu'un essaye de faire une attaque, elle échouera, puisqu'il ne connaît pas le jeton qui sera généré.

Dans notre exemple, il faudrait utiliser ceci :
Code : PHP
session_start(); // On démarre les sessions
$jeton = md5(uniqid(rand(), true)); // On génère le jeton
$_SESSION['jeton'] = $jeton;
Puis, il faudrait aussi modifier le lien pour supprimer un message, pour ajouter en paramètre la variable $jeton précédemment générée.
Par exemple, le lien pourrait donner quelque chose comme ça :
del-message.php?id=5&jeton=81561fcf997e3cdca00c98816eb3d05a
Et ensuite, dans le script del-message.php, il n'y à plus qu'a vérifier la validité du jeton :
Code : PHP
session_start(); // On démarre les sessions
if($_SESSION['jeton'] != $_GET['jeton'] || empty($_GET['jeton]))
 die('Jeton invalide !');
$_SESSION['jeton'] = '' // un jeton n'est valide qu'une fois !
// actions a effectuer…
Ainsi, le script va refuser de traiter la requête si le jeton envoyé est différent de celui généré, ou si aucun jeton n'a été généré.

Conclusion

Ainsi, comme vous avez pu le voir, la faille CSRF peut être très dangereuse, mais est pourtant méconnue.

Sachez que le célèbre GMail à été victime d'une faille CSRF.
De plus, il existe aussi un dérivé de cette attaque, nommé le cross-site printing, qui permet de contrôler des imprimantes !