Cet article est le troisième d’une série consacrée aux failles de sécurité dans le web, après un premier sur l’injection SQL et un deuxième consacré au cross-site scripting (XSS).
Le principe du CSRF
Les attaques de type cross-site request forgery sont un peu plus malicieuses que celles décrites dans les précédents articles.
Dans ce qu’on avait vu, qu’il s’agisse d’injection SQL ou de XSS, l’idée était globalement de faire entre des données corrompues dans un système, pour le pervertir de l’intérieur. C’est moche, mais quelque part c’est ce qu’on attend d’un hacker.
Les solutions à appliquer étaient presque simples : ne pas faire confiance, filtrer les données entrantes, et échapper les données sortantes.
Si je dis que le CSRF est plus malicieux, c’est parce que ce type d’attaque ne porte pas du tout sur le système visé. Aucune donnée pernicieuse n’est envoyée sur votre serveur, aucun code Javascript vérolé ne sera exécuté sur vos pages web.
Les attaques CSRF profitent de failles dans les systèmes d’authentification, et y ajoutent un peu de social engineering pour faire faire à vos utilisateurs des actions qu’ils ne souhaitent pas exécuter.
Prenons un exemple simple. Imaginons que vous ayez créé un site web sur lequel vous publiez des articles (pour l’exemple on imaginera que vous ayez tout développé vous-même plutôt que d’utiliser un CMS existant). Vous avez dû créer une petite interface d’administration, qui permet de créer des articles et de les effacer. Par-dessus, vous avez développé une couche d’authentification, pour restreindre l’accès aux seuls utilisateurs autorisés.
Votre système d’authentification repose peut-être sur la validation d’un couple identifiant/mot de passe. Lorsqu’un utilisateur s’est identifié correctement, vous déposez un cookie sur son navigateur. Ainsi, à chaque fois qu’il va sur une page d’administration, ou qu’il effectue une action à partir de l’interface d’administration, vous vérifiez le cookie avant d’autoriser l’affichage de la page ou l’exécution de l’action.
De cette manière, pour effacer l’article dont l’identifiant est le numéro 18, un administrateur cliquera sur le lien “Supprimer” correspondant. Son navigateur le conduira alors sur l’adresse “/admin/effacer/18”. Le serveur recevra en même temps le cookie servant à identifier l’utilisateur.
Si le cookie est valide, le serveur effacera l’article, puis redirigera l’utilisateur vers la page “/admin” pour le ramener à l’accueil de l’interface d’administration.
Pour que les utilisateurs n’aient pas besoin de se ré-identifier trop souvent, on fait en sorte que le cookie d’identification a une durée de vie assez longue, de plusieurs jours ou mois.
Maintenant, imaginons que notre administrateur clique sur un lien qui semble anodin, qu’il aura reçu dans un email, ou qu’il aura vu dans une publication sur un réseau social, ou encore dans une publicité ciblée. Sauf que l’adresse pointée, une fois visitée, redirige vers “http://notresite.com/admin/effacer/25”.
Qu’est-ce qu’il se passe ?
L’utilisateur étant identifié par son cookie (qui est toujours valide), l’article numéro 25 est effacé, et l’utilisateur ne s’en rendra même pas compte, car il aura été redirigé aussitôt vers la page d’accueil de l’administration.
Il se demandera pourquoi le lien sur lequel il a cliqué l’a amené là, mais pensera peut-être à une erreur de manipulation de sa part.
Vous voyez le côté pernicieux de la chose, et comment ça peut amener à faire exécuter des actions non voulues.
Pour être précis, le CSRF ne repose pas que sur des mécanismes de redirection. Certains hackers utilisent de faux sites de phishing ; par exemple un site qui ressemble à celui de votre banque. Lorsque vous cliquez sur un lien, il vous emmène sur le vrai site de votre banque, mais vers une adresse qui génère un transfert d’argent. Si vous avez un cookie valide, vous pouvez vous faire avoir.
Les solutions
Les attaques CSRF ne datent pas d’hier, elles sont bien connues. Sur le site du CERT (le Centre gouvernemental de veille, d’alerte et de réponse aux attaques informatiques), il y a une note d’information très complète sur le sujet qui a été publiée en 2008. Je vous en conseille d’ailleurs la lecture.
Plusieurs solutions ont été présentées au fil du temps, avec des niveaux de complexité et d’efficacité assez variables. Ce que je trouve amusant, c’est que les solutions les plus complexes ne sont pas les plus efficaces.
Regardons ça ensemble.
Un moyen de contourner le problème est d’utiliser un CAPTCHA sur toutes les opérations. Mais on est bien d’accord, c’est difficilement faisable en pratique (à moins de vouloir transformer vos interfaces d’administration en outils de torture pour administrateurs).
Un autre moyen, assez à la mode à une époque, consiste à générer un jeton temporaire. Ce jeton est passé en paramètre lors des appels au serveur, qui peut alors vérifier sa validité.
Ce jeton a une date de péremption qui peut être gérée de deux manières différentes : soit le serveur garde en base de données la liste de tous les jetons créés (et de leurs dates de péremption), soit les jetons sont générés de manière cryptographique (la date est visible en clair, et un hash lui est accolé ; le serveur pourra vérifier que le hash correspond bien).
Cette méthode est plutôt complexe à mettre en œuvre, même si plusieurs frameworks proposent des outils pour la mettre en place plus aisément.
Sinon, il y a des bonnes pratiques qui sont plutôt efficaces en la matière.
La première est de n’accepter que des requêtes POST pour les actions délicates. En effet, lors d’une redirection, le navigateur n’est capable de faire qu’une requête GET.
La seconde est de vérifier le contenu de l’en-tête REFERER envoyé par le navigateur. Lors d’une redirection, le REFERER contiendra un domaine différent de celui de votre site.
La troisième bonne pratique est d’utiliser le paramètre SameSite lors de la création du cookie d’authentification. Si ce paramètre est positionné à la valeur “Lax”, le cookie ne sera pas envoyé dans le cas d’une requête POST provenant d’un autre site. Avec la valeur “Strict” c’est encore plus restrictif, les utilisateurs seront obligés de passer par l’accueil de votre site avant de pouvoir effectuer une opération d’administration.
La bonne nouvelle, c’est que les navigateurs évoluent. Les versions récentes de Chrome considèrent que tous les cookies sont “Lax” par défaut (à moins que le paramètre SameSite n’ait été explicitement positionné à “None”).
Lorsqu’elles sont utilisées conjointement, ces bonnes pratiques sont très efficaces.