Introduction aux Cross Site Request Forgeries ou Sea Surf

Rechercher
Boutique en ligne, solution e-commerce, script PHP et PERL : RAYNETTE

Introduction aux Cross Site Request Forgeries ou Sea Surf

  • Par Emacs
  • 13 commentaires
  • 18656 lectures
  • RSS -  Atom

Vous connaissez peut-être les attaques XSS qui consistent à injecter du code malveillant, et où l'utilisateur est directement victime de l'action du code (boîtes de dialogue, redirections, vols de cookies, etc.) ? Eh bien, ce tutoriel va vous apprendre un autre type de faille radicalement opposé à celui-ci. Il s'agit bien entendu des attaques CSRF.

Présentation générale des CSRF

Beaucoup moins répandues que les injections SQL,les attaques CSRF (autrement prononcées « Sea Surf ») exploitent les utilisateurs et les rendent complices de l'attaque. Notez que ce genre d'attaqueest spécifique aux applications web puisqu'elles exploitent essentiellement le navigateur de l'utilisateur.

Le principe est enfantin : on incite un internaute à se rendre sur une page afin qu'une requête spécifique soit déclenchée à son insu. Ainsi, par le fait qu'il se soit rendu sur la page,l'internaute devient complice de l'attaque.

Bien entendu, l'internaute en question est connecté (par exemple sur son espace membre, un backoffice d'administration, son interface de gestion de son compte bancaire...), et c'est bien là tout le danger de la faille...

Un exemple concret

Prenons un exemple concret afin que vous compreniez mieux le principe de ces failles. Certains scripts d'annuaires permettent l'affichage des sites les mieux notés en fonction du nombre de votes. A partir de celà, il est possible de décortiquer le code de l'annuaire, de trouver la fonction qui incrémente la variable du nombre de votes et de s'en servir. On peut, par exemple, l'inclure en tant qu'image avec la balise <img /> et lui appliquer la propriété CSS display:none;. Cela nous donnerait quelque chose comme :

<div style="display:none;">
<img src="http://www.example.com/vote.php?id=45" />
</div>

Au chargement de la page contenant ce code HTML, le navigateur exécutera une requête GET sur la ressource distante dont l'url est spécifiée par l'attribut "src" de la balise <img/>. Vous le savez peut-être, mais il est possible de renvoyer une image à partir d'un script PHP. C'est pourquoi il n'est pas étonnant de voir apparaître une url avec une page PHP et des paramètres, à la place d'un chemin absolu vers une véritable image.

Le navigateur, ne se souciant pas du type de fichier distant, va tenter de charger et d'afficher la fausse image. Si cela échoue, tant pis... La requête sur le fichier php a tout de même été exécutée ! Le fait d'utiliser display:none; rendra l'image invisible aux visiteurs. Ainsi, vous pourrez gonflez votre nombre de votes sans que personnes ne s'en rende compte. L'exemple ci-dessus est quelque peu simplet, mais la portée d'action de la faille n'est pas des moindres. En se basant sur le même principe, on aurait pu vous faire transférer une somme d'argent de votre compte, vous faire acheter un produit, etc.

Quels sont les moyens pour se protéger des CSRF ?

La seule chose à savoir c'est qu'il n'existe pas de technique ou de fonction 100% fiable et ultime pour vous protéger des CSRF, contrairement aux XSS pour lesquelles il suffit d'échapper les données de sortie. Néanmoins, vous pouvez rendre la tâche un peu plus ardue à ceux qui essaieraient ce genre d'attaque.

Vérifiez vos referers

Envoyé par votre navigateur, le referer permet de déterminer la provenance des requêtes. La vérification du referer n'est pas une fin en soi car on peut bloquer son envoi ou encore le modifier. Néanmoins, vous pouvez vous en servir pour diminuer le nombre d'attaques par CSRF.

Les tokens, pensons-y !

Cette technique permet de spécifier une durée de vie au formulaire. L'astuce consiste donc à vérifier que le formulaire n'a pas été saisi dans un délai trop court (robot) ou bien trop long. Voici un exemple :

<?php
$token = md5(uniqid(rand(), TRUE));
$_SESSION['token'] = $token;
$_SESSION['token_time'] = time();
?>

A partir de cela, il faudrait un champ caché dans notre formulaire (type="hidden") qui aurait pour valeur $token. Avec ces informations, il suffit alors de faire une petite condition comme celle-ci :

Vérification du jeton appliqué à un formulaire
<?php
// Définition de la durée de vie du jeton
// Ici il est de 180 secondes soit 3 minutes
define('DELAI_MAXIMUM', 180);
if($_POST['token'] == $_SESSION['token']
&& (time() - $_SESSION['token_time']) <= DELAI_MAXIMUM)
{
actions();
}
else
{
die('Jeton expiré !');
}
?>

Vous pouvez également générer une suite de nombres aléatoires que vous mettrez dans chaque formulaire en champ caché (cf. paragraphe ci-dessus) A partir de cela, il faudra obligatoirement que cette suite de nombres soient contenue dans l'URL sans quoi votre code n'interprétera pas la requête. Vous devrez néanmoins choisir une suite de nombres assez longue afin que la chance qu'un pirate tombe sur un couple nombre / utilisateur valide soit infime.

Faites valider les actions critiques

Avant chaque action critique (ajout, suppression, édition, virement bancaire...), n'hésitez pas à soumettre un captcha à l'utilisateur afin d'être certains qu'il a bel et bien demandé l'exécution de la requête. Pour ne pas trop perdre en ergonomie, vous pouvez utiliser une vérification JavaScript (boîte de dialogue), mais cette pratique est très fortement déconseillée dans la mesure où l'exécution du script Javascript dépendra de la configuration du navigateur client.

L'un des plus bel exemple pour illustrer ces validations d'actions sont les formulaires dynamiques que l'on retrouve sur les sites bancaires. Il s'agit de formulaires qui vous demandent de saisir votre code avec la souris en pointant les chiffres adéquats (voir la capture ci-dessous).

Grille de saisie de password de la BNP Paribas

Bien sûr, l'ordre et la disposition des chiffres dans la grille sont générés aléatoirement à chaque rechargement de page. Quel en est l'intérêt ? La disposition aléatoire permet tout d'abord de limiter les CSRF mais également de ralentir les attaques avec les nouveaux systèmes capables de détecter et d'enregister les positions du curseur sur l'écran (via un logiciel ou un javascript intégré avec une XSS).

Configuration de PHP et bonnes habitudes de programmation

Limitez l'utiliation de $_GET et desactivez les register_globals sur votre serveur. Préférez également toujours le traitement des actions importantes avec la méthode POST plutôt que la méthode GET. Vous limiterez ainsi fortement les risques d'attaques CSRF puisqu'elles se propagent généralement via des injections XSS et des URL.

Utilisez également des captchas, demandez une seconde saisie du mot de passe ou effectuez des vérifications javascript pour valider les actions importantes (achat, vente, transfert) sur votre site. L'ergonomie en souffrira quelque peu, mais c'est le prix à payer pour limiter les risques.

Remarque : notez au passage que ces conseils sont en contradiction avec la philosophie du « Web 2.0 » où les développeurs s'efforcent de limiter le nombre d'écrans pour obtenir des interfaces ergonomiques et « user friendly » (ami avec l'utilisateur). L'utilisation massive d'Ajax peut conduire à de nombreuses failles XSS et CSRF, et c'est pour cette raison que des sites comme Facebook, Twitter... sont la cible quotidienne de pirates malintentionnés.

Comme il l'a été évoqué au début du tutoriel, il faut que l'utilisateur soit authentifié pour que l'attaque représente unintérêt pour le pirate. Parconséquent,vous pouvez commencer par limiter la durée de vie des sessions sur votre serveur en modifiantla directive session.gc_maxlifetime de votre php.ini.

Conclusion

Vous savez donc désormais ce qu'est une attaque par CSRF ainsi que différents moyens techniques pour vous en protéger. N'hésitez donc pas à les appliquer.Pour ceux qui se le demandent encore, je n'ai volontairement pas donné d'exemples véritablementnuisibles pour ne pas vous tenter de les mettre en oeuvre ;)

Auteur

Cet article a été rédigé par Damien Carret (puis amélioré par Hugo Hamon [Emacs]). Damien Carret étudie actuellement dans le domaine informatique et les technologies des systèmes d'information. Principalement autodidacte et passionné de PHP, il s'intéresse principalement aux questions de sécurité qui touchent les applications web. Ses connaissances en informatique l'ont amené à publier des articles pour le magazine Hackin9 disponible en kiosque chaque mois.



Les commentaires

1. Par Paul Henri Bonnement le 02/03/2008 00:06

Tutoriel fort intéressant, qui illustre rapidement la difficulté de se protéger efficacement contre ces attaques.

2. Par Philippe Gamache le 02/03/2008 16:45

La technique bancaire comporte un problème d'accebilité. Elle est pas utilisable par les personnes a visibilité et motricité réduite.

3. Par Emacs le 02/03/2008 19:02

@Philippe: oui c'est malheureusement le gros défaut de cette technique... Mais existerait-t-il dans ce cas une meilleure technique à la fois ergonome et efficace contre les CSRF ?

4. Par pluriels le 04/03/2008 17:29

Parmi les choses que j'ai lues : - vérifier si le requête vient du bon serveur / domaine. Cela empêche de lancer une requête depuis un site extérieur.

- sur son propre site : utiliser un token est intelligent.
filtrer correctement tout contenu que l'utilisateur peut mettre en ligne. Code dans les textarea ou encore fichiers attachés.
Faire bien attention à filtrer à la saisie, mais aussi à l'affichage.

5. Par Zelig le 09/05/2008 12:24

Salut,
Je me demande si on peut faire autre chose.
Imaginer, je suis un moderateur d'un site et je veux supprimer des utilisateurs:
Quand j'essaye de valider des utilisateurs à supprimer, je lance un widget de confirmation, genre fenetre de confirmation, mais à son lancement, elle charge un captcha, juste apres son declenchement qui est ajouter à un element hidden, juste avant de valider le formulaire.
Comme ça en plus du token, j'ai un captcha qui est lancé à la dernière seconde, pour confirmer les transactions sensibles. Déja c'est difficile d'accéder à l'espace admin, mais en plus il ya un captcha qui est découvert à la dernière seconde.
Qu'en pensez vous?

6. Par Emacs le 09/05/2008 13:41

Oui la solution tocken + captcha est une bonne idée pour sécuriser davantage l'application contre les CSRF

7. Par okens le 06/07/2008 02:29

Juste une petite faute dans le paragraphe : "Configuration de PHP et bonnes habitudes de programmation".
Je cite : "[...] mais c'est le prix à payer limiter les risques. "

8. Par Emacs le 06/07/2008 13:03

Merci c'est corrigé

9. Par 1pas100 le 16/10/2008 22:05

Explications claires et agréables à lire. Comme la présentation du site. C'est à ce jour la seule page que j'ai lu mais cela donne envie de revenir.

10. Par bruno le 14/03/2009 05:34

Bonjour

Le pirate peut copier le token manuellement

11. Par capoeiradance le 07/04/2009 11:59

Bonjour,

tu proposes donc quoi Bruno?

12. Par audax le 05/09/2009 00:58

Le token peut aussi être evoyer par un autre moyen que la connextion internet, ie par exemple par par sms, par téléphone sur serveur vocal ou par courrier papier avec une table de token à usage unique.

13. Par bruno le 07/09/2009 13:31

Un token est une bonne défense contre les robots automatisés , il est donc nécessaire de l'intégrer . Mais il faut aussi rendre le formulaire de réception inépendant , sans connexion mysql , et l'intégrer avec un include de manière a éviter les bypasses de formulaires , envoi direct des données a la réception , idebtifiants cryptés par exemple , il est primordial , que le client remplisse manuellement les champs et ne puisse outrepasser la saisie manuelle . On peut aussi inclure des define dans la soumission et des defined ? a la réception , ou autre script interdisant l'accès direct au formulaire de réception , ou faire un exit si la connexion est impossible , la connexion mysql se trouvant dans le formulaire de saisie .

On peut imaginer pas mal de choses .