web-dev-qa-db-fra.com

Dois-je implémenter un délai de mot de passe incorrect dans un site Web ou un service Web?

Avec des arguments exprimés en cette réponse , il y a un délai de quelques secondes entre l'utilisateur entre un mot de passe incorrect et quand il/elle apprend réellement, ce mot de passe était incorrect. Cette solution de sécurité est implémentée dans un système d'exploitation (ici un OS élémentaire ) et dans commandes de console comme Sudo etc.

Dois-je mettre en œuvre le même mécanisme dans mon site Web ou mon service Web? Ou puis-je facilement supposer qu'un retard typique dans l'échange d'informations entre le navigateur et le serveur sera suffisant pour bloquer les attaques de force brute gonflées (cela prend plus d'une seconde même sur le système local entre appuyer sur le bouton Login sur mon site jusqu'à ce qu'une information sur un mot de passe incorrect soit renvoyée; cela semble assez long AFAIK).

Il y a ne question similaire à ce sujet. Cependant, les deux réponses ( this et this ) ne satisfont pas ma question ou sont même un peu hors sujet. Je ne pose pas de question sur la suspension ou le verrouillage temporel du compte de l'utilisateur après chaque échec de connexion (et donc les arguments sur le verrouillage de l'attaquant empêchant l'utilisateur réel de se connecter sont désactivés) . Je parle uniquement du délai possible entre l'affichage à nouveau du formulaire de connexion .

24
trejder

Je suppose que votre intention avec le délai d'échec est d'empêcher les attaques par force brute: si un attaquant essaie de deviner le mot de passe d'un utilisateur, il échouera d'abord plusieurs fois; si nous pouvons prolonger considérablement ces échecs, cela rendra l'attaque d'un ordre de grandeur plus difficile, et donc peu susceptible de réussir (dans un délai raisonnable).

Toutefois.

Cela suppose que l'attaquant attend patiemment entre les tentatives de connexion. Et, comme nous le savons tous, les cyber-pirates sont des gens très polis et patients.
Cela dit, certains attaquants peuvent choisir de NE PAS attendre, et d'envoyer simplement de nombreuses demandes en parallèle. Si une tentative de connexion ne reçoit pas de réponse immédiatement, l'attaquant peut interpréter cela comme une tentative infructueuse, tuer cette demande et passer à la suivante.

Un avantage supplémentaire de ce schéma est qu'il serait beaucoup plus facile pour un attaquant de créer une charge déraisonnable sur le serveur (il suffit d'envoyer un grand nombre de demandes de connexion défaillantes, chacune occupera un thread pendant plusieurs secondes ...), et éventuellement réussir même à DoSing le serveur.

En fait, le principal problème de cette solution est qu'elle n'empêche PAS précisément de nombreuses demandes parallèles. Si un attaquant tente une attaque par force brute en ligne, il ne va pas s'asseoir devant le clavier en tapant plusieurs mots de passe, l'un après l'autre, aussi vite que Hugh Jackman le peut - si tel était le cas, vous ne seriez que à risque si l'utilisateur a un mot de passe simple mort.
En réalité, elle aura un script ou un outil automatisé qui enverra (presque) autant de requêtes que le serveur peut gérer, puis continuera. Le risque n'est pas que quelqu'un essaie 30 mots de passe différents par minute, c'est 1000 mots de passe par minute ou 10 000.

La manière niquement d'empêcher cela est la limitation de l'utilisateur/le verrouillage du compte/la suspension incrémentielle - appelez-le comme vous voulez, c'est fondamentalement la même idée d'autoriser le nombre X de tentatives de connexion par compte dans un délai donné.
Donc, même si cela ne se réduit pas à "une seule tentative de connexion à la fois", il se rapproche suffisamment.

34
AviD

Une partie du problème est que le maintien de la connexion ouverte pendant que vous attendez l'expiration du délai utilise des ressources précieuses, en particulier dans certaines configurations populaires où le nombre de connexions simultanées autorisées est assez minime.

Une solution idéale consiste à concevoir votre système comme suit:

  • Concevez d'abord la logique dans l'interface utilisateur. Une connexion échouée renvoie immédiatement l'état d'échec, mais il y a un délai avant la réinitialisation de l'interface utilisateur pour permettre une tentative de connexion supplémentaire. Ce n'est pas pour appliquer la règle, mais plutôt pour s'assurer qu'un utilisateur bien élevé ne voit jamais de message d'erreur.

  • Appliquez la logique sur le serveur principal en renvoyant immédiatement un état d'erreur pour toutes les tentatives de connexion pendant la période de "refroidissement". Ne vérifiez même pas si le mot de passe est correct, échouez immédiatement tout en consommant le moins de ressources possible.

  • Pour tester si une tentative de connexion est encore autorisée, la meilleure solution consiste à utiliser quelque chose de léger, comme memcached. Par exemple, après une tentative infructueuse avec un nom d'utilisateur donné, stockez dans memcached l'heure à laquelle le refroidissement expire. Ensuite, lors de la mise en service de nouvelles tentatives de mot de passe, vérifiez l'entrée dans memcached pour voir si la période de refroidissement est encore terminée.

  • Appliquez la même logique sur une base par IP, pas seulement par nom d'utilisateur. Je ne devrais pas être capable de deviner des milliers de mots de passe par seconde rien qu'en tournant à travers les noms d'utilisateur.

  • Implémentez une interruption exponentielle. C'est un peu de crédit supplémentaire, mais 2 tentatives en 1 seconde ne sont pas un gros problème, mais 50 tentatives en 5 minutes sont un gros problème. Cela peut être un peu plus difficile à concevoir, mais des échecs répétés devraient entraîner des temps de réinitialisation plus longs. Cela pourrait être aussi simple que de conserver un compteur de pannes (encore une fois, dans memcached) et de calculer le prochain refroidissement en fonction de ce compteur. La logique par IP devrait cependant être un peu différente, car vous ne voulez pas que l'attaquant puisse réinitialiser son compteur simplement en envoyant les informations d'identification d'un utilisateur connu.

7
tylerl

Vous voulez que votre serveur fasse le moins de travail possible, pour éviter de faire DoSing vous-même. Le verrouillage de compte est idéal pour DoSing vos utilisateurs.

si nombre (authentifications infructueuses pour le nom d'utilisateur U)> seuil, la demande est alors résolue CAPTCHA

si comptage (authentifications infructueuses pour le mot de passe P)> seuil, puis demande CAPTCHA résolue

si vous n'aimez pas CAPTCHA alors exigez Preuve de travail

(faire calculer au client les facteurs premiers d'un vrai grand nombre)

6
Neil McGuigan

Un délai traditionnel atténuerait les attaques basées sur un navigateur Web, par exemple, si quelqu'un utilise phantomjs pour automatiser les tentatives de connexion, le délai normal (dans votre cas "plus d'une seconde") serait suffisant pour empêcher quiconque d'essayer de forcer brutalement un mot de passe, il suffit prend trop de temps.

Cependant, la plupart des attaques par force brute ne sont pas exécutées dans les navigateurs Web, mais dans des environnements scriptés où elles peuvent émettre plusieurs demandes en même temps.

La façon dont je le mettrais en œuvre est d'avoir un retard intentionnel du côté serveur après une connexion infructueuse, plus un mécanisme de limitation (quelque chose comme this ) et un message frontal ou un spinner qui informe l'utilisateur que le les informations d'identification sont en cours de vérification (personne ne veut une page vierge pour un rechargement ou un système apparemment suspendu pendant pendant une longue période )

4
Purefan