Limiter le nombre de tentatives de connexions sous Symfony

written by Romaric 18 avril 2019

Aujourd’hui nous allons parler d’une mesure de sécurité très simple dans son approche. Nous voulons limiter le nombre d’essais de connexion pour un nom d’utilisateur. Le but est d’empêcher une attaque brute-force, où un attaquant qui connaît ou devine un nom d’utilisateur va essayer tous les mots de passe possibles. Bien que très peu sophistiquée, cette attaque est courante, par exemple elle serait à la source de la dévastatrice fuite de photos de personnalités en 2014. La mesure de protection est simple dans son concept, et nous allons voir, simple à mettre en place.

Choix de l’approche

Pour cette première approche, nous voulons empêcher plus de 3 tentatives de connexion avec le même nom d’utilisateur (ou adresse e-mail) en l’espace de 10 minutes. Ces valeurs semblent raisonnables point de vue utilisateur, et cela permet de rendre une attaque brute-force trop coûteuse en temps: au maximum, 432 mots de passe pourraient être testés en 24 heures. On pourrait vouloir une approche plus dure, qui bloque la connexion de l’utilisateur après X tentatives, mais alors il faut prévoir et mettre en place une procédure permettant à l’utilisateur légitime de reprendre possession de son compte. Sinon il sera facile à un attaquant de bloquer uns à uns tous les administrateurs ou tous les utilisateurs de notre plate-forme, ce qui est aussi destructeur!

Deuxième aspect important, nous voulons logguer les tentatives de connexion, afin de pouvoir monitorer et analyser ce qui se passe sur notre production, de détecter s’il y une activité suspecte. Pour une première version simple, nous allons utiliser une entité Doctrine, à défaut d’outils plus évolués. Cela sera suffisant pour une application web au traffic faible à modéré.

Code avec Symfony 4

Nous allons commencer par l’entité. Elle est relativement basique, on stocke la date de la tentative, le nom d’utilisateur envoyé, et l’adresse IP (pour détecter les attaques très basiques). Pas besoin de setters, l’entité est immutable, on ne va pas la modifier une fois construite.
Ne pas oublier de générer une migration Doctrine (bin/console make:migration) et de l’exécuter une fois l’entité créée!

Pour la suite, nous partons du principe que votre authentification est gérée par un Guard, similaire à ce que génèrerais make:auth du Symfony Maker. Dans notre exemple, on utilise « email » comme nom d’utilisateur, mais vous pouvez adapter facilement. Seules les méthodes getCredentials et checkCredentials sont modifiées, le code ci-dessous est commenté avec des explications:

Finalement, il reste à implémenter la méthode LoginAttemptRepository::countRecentLoginAttempts(). Ici le critère est simple, avec un seul délai, libre à vous de faire quelque chose de plus évolué.

Le résultat

Bilan

Notre application est maintenant protégée! Libre à vous d’aller plus loin, avec par exemple des délais exponentiels (le compte serait bloqué 10 minutes, puis 20mn à la seconde violation…) ou alors avec une tâche qui supprimera automatiquement les données en base après 30 jours.

Rendez-vous la semaine prochaine, la série d’articles sur la Sécurité avec Symfony continue 🙂

You may also like

6 comments

Alladin 16 décembre 2019 at 19 h 46 min

Bonjour et merci pour ce tutoriel c’est très bien expliqué !
Je voudrais savoir comment on fait pour appliquer la même chose quand on a utilisé FOSRestBundle.
Merci !

Reply
Alladin 17 décembre 2019 at 22 h 11 min

J’ai déjà fait ça merci à vous

Reply
Ladinstar 17 décembre 2019 at 22 h 10 min

« (le compte serait bloqué 10 minutes, puis 20mn à la seconde violation…) » j’aimerais faire ainsi mais je ne sais pas vraiment comment je peux le faire. J’aimerais avoir une sorte d’indication pour le faire. Merci

Reply
distributeur de savon en cuivre 10 février 2021 at 3 h 21 min

Très intéressant, merci

Reply

Leave a Comment

*