En quoi consiste le rate limiting ?

Rate limiting fonctionnement implementation

Lors de nos pentests sur des plateformes web, un des principaux vecteurs d’attaque que nous utilisons le plus souvent pour découvrir et exploiter des vulnérabilités est le manque de rate limiting (ou limitation de débit).

Partant de ce principe, nous estimons que toute application web se verra, tôt ou tard, confrontée à une attaque générant beaucoup de trafic. Celles-ci peuvent se présenter sous plusieurs formes, mais les principales sont les suivantes :

  • Les attaques DoS (déni de service) de masse, consistant à générer énormément de requêtes afin de surcharger le serveur et ainsi l’empêcher de traiter les requêtes légitimes.
  • Les attaques par déni de service plus ciblées, consistant à appeler à répétition des endpoints et des fonctions de l’application gourmandes en ressources dans le même but que l’attaque précédente.
  • Les attaques brute force, consistant à tenter de nombreuses combinaisons login/mot de passe, afin d’obtenir une session sur la plateforme et ainsi voler un compte.
  • Les attaques d’énumération, consistant, comme son nom l’indique, à énumérer les ressources de la plateforme en tentant d’itérer sur celles-ci. Cela peut être une liste d’ID, des noms de fichiers, des noms d’utilisateurs, etc.

Toutes ces attaques peuvent être mitigées via la mise en œuvre d’un processus essentiel qui permet de limiter la quantité de requêtes qu’un client peut envoyer au serveur : le rate limiting.

Cette limitation de débit peut prendre plusieurs formes, que nous allons voir plus en détail. Mais avant cela, nous vous présentons quelques exemples concrets d’attaques brute force ayant permis la découverte et l’exploitation de vulnérabilités critiques grâce au manque de rate limiting.

Implémenter le rate limiting pour contrer les attaques brute force

Manque de rate limiting, brute force de token et prise de contrôle d’un compte utilisateur

Lors d’un pentest d’une application web, nous avons observé que comme sur la plupart des plateformes il existait une fonctionnalité de réinitialisation de mots de passe. Ici, lorsqu’un utilisateur effectuait une demande de réinitialisation, un mot de passe temporaire de 6 chiffres était généré puis lui était envoyé par email.

Login : [email protected] 
Mot de passe : 558573 

Login : [email protected] 
Mot de passe : 922145 

Login : [email protected] 
Mot de passe : 498144 

Comme vous pouvez le voir, chaque mot de passe généré consistait en un nombre aléatoire de 6 chiffres. Tester toutes les possibilités d’une liste à 6 chiffres semble faisable si l’application web ne nous limite pas, et c’était bien le cas ici. En effet, le serveur ne possédait pas de protection de rate limiting, ce qui signifie que rien ne bloquait les tentatives de connexion d’un utilisateur.

Nous avons donc pu tester toutes les possibilités jusqu’à trouver le mot de passe temporaire. L’impact de cette attaque est assez critique car un attaquant peut prendre le contrôle de n’importe quel compte de la plateforme en connaissant l’email de la victime (ce qui n’est pas très difficile à retrouver via une petite reconnaissance sur le web).

Manque de rate limiting, brute force de mots de passe et de 2FA

Il est très facile d’exploiter un manque de rate limiting pour, par exemple, bruteforcer un mot de passe via un formulaire de connexion. Il suffit à l’attaquant d’essayer un par un des mots de passe qu’il aura auparavant compilés et/ou générés dans un fichier.

Dans l’exemple ci-dessus, on peut voir que le serveur répond différemment dès lors que l’on envoie une requête de connexion avec « peter » comme mot de passe. Celui-ci est valide et nous sommes désormais connectés avec un compte qui ne nous appartient pas.

La même tactique s’applique à un processus de 2FA (authentification 2 facteurs) qui demande par exemple un code à 4 chiffres envoyé par SMS. En cas de manque de rate limiting, il suffit d’essayer toutes les combinaisons possibles pour deviner juste.

Quelles sont les techniques d’implémentation de rate limiting ?

Le rate limiting global

Cette limitation se situe généralement au niveau du serveur (ou du proxy qui réceptionne les requêtes en premier lieu, le cas échéant) et consiste à tout simplement abandonner (« drop ») les requêtes provenant d’IPs qui en génèrent beaucoup trop.

Cette limite est généralement haute, plusieurs dizaines de requêtes par seconde, et est principalement là pour contrer les attaques par déni de service de masse. Elle est vitale pour n’importe quelle plateforme et est généralement incluse dans les offres d’hébergement moderne.

Mais elle est malheureusement insuffisante pour contrer les attaques plus ciblées. Pour cela, il nous faut…

Le rate limiting ciblé

Cette limitation consiste à réduire plus drastiquement les requêtes reçues et traitées sur certains endpoints. Elle doit être implémentée au niveau applicatif, généralement via un middleware et permet de cibler et de moduler beaucoup plus précisément les limitations de débit au cas par cas, en autorisant, par exemple, seulement 5 tentatives de connexion, ou en interdisant plusieurs appels à une fonction gourmande en ressource tant que les opérations précédentes ne sont pas terminées.

Le client peut être identifié par son IP, comme pour le rate limiting global, mais peut aussi être identifié par son jeton de session, une fois connecté, si les endpoints protégés sont accessibles uniquement par un utilisateur connecté bien sûr.

Dans le cas d’endpoints consommant beaucoup de ressources, il est également possible de mettre en place une file d’attente, afin de s’assurer que les demandes soient traitées séquentiellement et non en parallèle (ou alors uniquement un certain nombre). Dans ce cas-là, il faut évidemment également s’assurer qu’un utilisateur ne puisse pas complètement remplir la file d’attente à lui tout seul.

En pentest, nous avons par exemple rencontré une fonctionnalité permettant de générer un PDF à partir de données JSON. Le fait de lancer la génération de nombreux PDF en simultané a complètement saturé la mémoire du serveur et rendu indisponible le service. Dans ce cas-là, une file d’attente peut être mise en place.

Il existe cependant un cas problématique : que faire si le comportement de l’utilisateur est suspect mais potentiellement « normal » ? Par exemple, dans le cas où 5 tentatives de connexions infructueuses ont été effectuées, est-ce juste un utilisateur ayant oublié son mot de passe ou un acteur malveillant tentant de voler un compte ? Que faire ?

Gestion des bots et rate limiting : l’exemple des CAPTCHAs

L’immense majorité des attaques mitigées par le rate limiting seront effectuées par des robots. Aucun attaquant ne tentera manuellement les 1 000 000 de mots de passe qu’il a identifiés comme plausibles pour tenter de voler un compte : il délèguera la tâche à une machine.

Or, les machines ont une faiblesse : elles arrivent très difficilement à reconnaitre des motifs dans des images « chaotiques », ce qui est exactement ce que demande un CAPTCHA (Completely Automated Public Turing test to tell Computers and Humans Apart) : un petit formulaire qui bloque l’envoi de la requête tant que le client n’a pas reconnu le motif en question, ce qui peut être une suite de lettres et de chiffres, un cercle ouvert parmi des cercles fermés, des panneaux, un type de véhicule, etc.

Ci-dessous un exemple de CAPTCHA :

Exemple Captcha

Si vous avez trouvé le cercle ouvert, félicitations, vous êtes humain !

Certains CAPTCHAs plus avancés étudient le comportement de l’utilisateur (mouvement de souris, vitesse de frappe…) pour s’assurer qu’il est bien humain : pas besoin d’images ou de puzzles.

Les CAPTCHAs peuvent être implémentés sur des fonctionnalités qui doivent être sévèrement régulées (connexion, oubli de mot de passe, fonction très gourmande en ressource…) mais qu’il n’est pas exclu qu’un utilisateur puisse appeler plusieurs fois en un temps relativement court.

Fréquence

Une question reste cependant en suspens : quelle fréquence autoriser pour chaque endpoint ? Globalement ?

Il n’y a pas de réponse toute faite : tout va dépendre de votre application et de l’utilisation que vont en faire vos utilisateurs. Chaque endpoint sensible doit être étudié individuellement pour définir quelle fréquence peut être acceptable.

L’ajustement du rate limiting est un processus constant, qui doit se nourrir de vos données analytiques, de vos retours clients et de votre système de logging et monitoring pour s’assurer que les attaques soient bloquées tout en ne contrevenant pas à l’expérience de vos utilisateurs et à l’utilisation légitime de la plateforme.

Conclusion

Le rate limiting fait partie des mesures de sécurité essentielles qui doivent être mises en place sur une application web. Il protège de nombreuses attaques dangereuses et son implémentation permet aussi de s’assurer un plus grand contrôle sur l’utilisation faite des différentes fonctionnalités d’une plateforme. Correctement implémenté, il permettra de garantir la disponibilité de vos serveurs et applications web.