Alternatives pour envoyer un mot de passe en clair lors de la connexion

Jan 02 2021

Remarque: j'ai déjà lu Est-il possible d'envoyer un mot de passe en texte brut via HTTPS? et la sécurité https - le mot de passe doit-il être haché côté serveur ou côté client? , mais ici il s'agit d'une méthode de remplacement spécifique (voir ci-dessous).


Après avoir lu un article sur une nouvelle méthode d'authentification sur le blog Cloudflare , j'ai regardé les POSTrequêtes qui sont envoyées lors de l'authentification avec "Developer Tools> Network". De nombreux sites Web populaires (Reddit, HN, etc.) envoient toujours le mot de passe en texte clair dans la POSTdemande ( sécurisée par SSL) (voir capture d'écran ci-dessous).

Cette méthode de connexion est-elle toujours la norme de l'industrie?

L'alternative suivante est-elle plus sécurisée que l'envoi d'un mot de passe en clair via HTTPS?

  • signup: le client génère un aléatoire salt, et envoie le tuple (username, salt, hash(plain_password + salt))via une POSTrequête. Ensuite, le mot de passe en clair n'atteint jamais le serveur.

  • connexions suivantes: le serveur doit renvoyer saltà tout client qui tente de se connecter avec un donné username, afin que le client puisse hacher avec le même sel. Cela signifie donc que le serveur divulgue saltà quiconque tente de se connecter avec un nom d'utilisateur donné.

  • avantage: le serveur stocke un mot de passe salé + haché (ce qui est standard) mais aussi le serveur n'a jamais, jamais vu le mot de passe en clair, même une fois (donc si le serveur est compromis, le risque est limité)

Remarques:

  • puisque H = hash(plain_password + salt)maintenant se comporte un peu comme un nouveau plaintext(voir la 2ème réponse de Zero Knowledge Password Proof: pourquoi le hachage du mot de passe côté client n'est pas un ZKP? ), alors le serveur peut stocker (username, salt, server_salt, hash(H + server_salt))dans la base de données, au lieu de (username, salt, H).

  • pour atténuer les risques d'attaques de relecture, le serveur peut également envoyer un unique nonce, avec saltpour chaque connexion, qui expire après une tentative de connexion

  • l'objectif principal ici est que le serveur n'ait jamais accès au mot de passe en clair ou à un simple hachage de celui-ci (qui pourrait souvent être inversé avec une seule table arc-en-ciel pour l'ensemble du site). Je suis d'accord avec le risque qu'un attaquant doive calculer une table arc-en-ciel par utilisateur .

  • Exemple d'attaque que je voudrais atténuer: si le serveur a accès à un mot de passe en texte brut et qu'il est compromis (exemple Spectre / Meltdown vuln.), Le mot de passe en clair de l'utilisateur (éventuellement réutilisé sur d'autres sites Web) pourrait être volé, avant d'être salé -hashed et enregistré dans la base de données.


Réponses

14 SteffenUllrich Jan 02 2021 at 14:38

Je ne vois pas en quoi votre proposition est meilleure que les approches de hachage côté client existantes, mais je la trouve plus complexe à mettre en œuvre que d'autres. Malheureusement, vous ne décrivez pas un risque spécifique auquel vous essayez d'accéder, je suppose donc simplement les menaces typiques couramment observées.

Attaquant Man in the Middle

Dans ce cas, il est supposé qu'un homme au milieu a accès au trafic, par exemple parce qu'il a compromis une interception TLS de trafic de confiance dans un pare-feu d'entreprise ou a mis la main sur une autorité de certification de confiance comme dans le cas de superfish .

Dans ce scénario, l'attaquant a accès à Hla même chose qu'auparavant avec plain_password. Puisque Htout ce qui est nécessaire pour l'authentification, l'attaquant réussit et votre approche n'ajoute aucune protection supplémentaire ici .

Masquage des mots de passe faibles et réutilisation des mots de passe

Un argument courant pour le hachage côté client est de ne pas exposer un mot de passe faible ou réutilisé au serveur, mais plutôt de s'authentifier avec un mot de passe dérivé complexe. Votre approche le fait avec le hachage plain_passwordavec certains utilisateurs générés de manière aléatoire salt, puis envoyez Het saltau serveur lors de la configuration du mot de passe.

Bien que cela fonctionne, chaque authentification nécessite maintenant une étape supplémentaire : d'abord, il doit récupérer le sel précédemment utilisé pour l'utilisateur auprès de l'utilisateur, puis il peut l'utiliser saltpour hacher le fichier plain_password. Cette étape supplémentaire rend l'authentification plus complexe puisqu'elle doit d'abord vérifier l'utilisateur auprès du serveur et plus tard, elle peut vérifier le mot de passe. De plus, une implémentation triviale de ceci ouvre une fuite d'informations puisqu'elle permet de vérifier si l'utilisateur existe en premier lieu (sel retourné ou non) sans autre authentification.

Cette fuite d'informations peut être fermée par le serveur renvoyant du sel, que l'utilisateur existe ou non. Bien sûr, cela ne peut pas être juste un sel aléatoire car sinon un attaquant pourrait simplement vérifier deux fois le même utilisateur et conclure que l'utilisateur n'existe pas si le sel retourné était différent. Donc, le sel doit en fait être fixé pour l'utilisateur non existant, c'est-à-dire dérivé du nom d'utilisateur.

Et cela montre également un chemin pour simplifier votre approche : au lieu de générer un sel aléatoire par l'utilisateur, de le stocker sur le serveur et de le récupérer plus tard, on pourrait simplement dériver le sel du nom d'utilisateur côté client . Un simple salt=hash(username+domain)serait suffisant pour générer un sel qui est unique par domaine et ainsi rendre les deux saltet Hdifférents même si usernameet plain_passwordêtre réutilisé sur des domaines différents. Et contrairement à votre approche, aucun déplacement supplémentaire vers le serveur n'est nécessaire pour récupérer d'abord le sel précédemment utilisé pour l'utilisateur.


En bref: cette approche simplifiée consiste essentiellement à envoyer hash(plain_password+username+domain)au lieu du mot de passe d'origine. Le domaine est ajouté pour s'assurer que même si usernameet plain_passwordsont réutilisés sur plusieurs sites, le mot de passe dérivé n'est pas réutilisé.

8 mti2935 Jan 02 2021 at 21:33

C'est exactement le problème que des protocoles comme PAKE et SRP visent à résoudre. Avec PAKE / SRP, le client et le serveur s'authentifient mutuellement en fonction d'un mot de passe connu du client (et d'une dérivation du mot de passe connu du serveur).

Le client démontre au serveur qu'il connaît le mot de passe, sans que le client envoie le mot de passe (ou les données équivalentes au mot de passe) au serveur. À la fin du processus, le client et le serveur partagent un secret partagé.

Le serveur ne stocke pas le mot de passe (ou les données équivalentes au mot de passe) et n'est pas sensible aux attaques par dictionnaire. Un espion ou un homme du milieu capable de voir le texte en clair envoyé sur le câble ne peut pas obtenir suffisamment d'informations pour obtenir le mot de passe. Cela empêche efficacement les attaques de type «man-in-the-middle» utilisant de faux certificats et empêche les sites de «phishing» de voler les mots de passe des utilisateurs.

Pour une bonne description de la façon dont 1password a implémenté SRP, voir https://blog.1password.com/developers-how-we-use-srp-and-you-can-too/

5 mentallurg Jan 02 2021 at 16:00

En plus de la réponse de Steffen Ullrich :

Si lors de la connexion l'utilisateur envoie uniquement le hachage, l'attaquant n'a pas besoin de connaître le mot de passe. Il suffit de voler la base de données de mots de passe. Ensuite, lors de la demande de connexion, l'attaquant enverra simplement le hachage de la base de données. Le serveur ne distinguera pas si le client a utilisé le mot de passe et l'a haché, ou si le client (attaquant) a simplement envoyé le hachage.

L'article sur OPAQUE aborde également ce problème: voler la base de données de mots de passe n'aidera pas l'attaquant. Il faudrait connaître le mot de passe simple de l'utilisateur.

3 MargaretBloom Jan 03 2021 at 08:43

Si l'attaquant a compromis votre serveur, il contrôle non seulement le logiciel exécuté sur votre serveur, mais également le logiciel exécuté sur les clients.
Quel que soit le schéma d'authentification magnifiquement conçu que vous avez conçu, l'attaquant peut le modifier avant qu'il ne soit envoyé au navigateur.
Vous avez maintenant un problème œuf-poulet: vous ne pouvez pas sécuriser un mot de passe si l'attaquant contrôle la façon dont il est collecté et envoyé à votre serveur.

Si vous vous inquiétez d'une violation de données, votre méthode fonctionnerait comme une protection, tout comme un serveur de hachage de mot de passe approprié.

Si vous vous inquiétez des attaques MITM, TLS les résout.
Si vous vous inquiétez des attaques MITM sur TLS, alors, comme j'aime à le dire, une bonne défense contre elles commence toujours par un manuel de Krav Maga. Un attaquant qui a suffisamment de ressources pour briser le TLS de manière cohérente n'a aucun problème à obtenir ce qu'il veut de toute personne non correctement formée et spécialement formée (oui, je parle de torture, de chantage, d'enlèvement et de meurtre).

Si vous vous inquiétez d'un acteur de la menace qui ne peut lire que les données reçues par le serveur, votre approche (telle que corrigée par Steffen) fonctionnera contre lui. Cependant, il s'agit d'une circonstance étrange et rare, souvent due à un serveur grossièrement mal configuré et à de mauvaises pratiques de développement (c'est-à-dire l'envoi d'informations d'identification sur les requêtes GET et le stockage public du journal d'accès). Il est plus facile de corriger ces erreurs que d'inventer un protocole juste pour y remédier.

Notez que les deux vulnérabilités que vous avez mentionnées (en fait, c'est juste une, car Meltdown est techniquement une variante de Spectre) entraîneraient éventuellement une élévation de privilèges locaux, donnant à l'attaquant le contrôle total de votre serveur Web. Soulignons à nouveau à quel point le scénario où un attaquant a un accès en lecture seule aux données reçues par votre serveur Web est rare.

Donc, la raison pour laquelle de nombreux grands sites ne l'utilisent pas, c'est parce qu'il n'ajoute à peu près rien, mais dans des circonstances spécifiques qui sont très probablement des erreurs de configuration. Il est également intéressant de noter que si un attaquant peut lire quelles données transitent sur votre serveur, vous êtes loin du côté perdant du jeu. Remarquez-moi, c'est bien d'avoir des protections en couches, mais votre objectif principal n'est pas que cela se produise en premier lieu. Et vous concentrer sur cela vous éviterait également d'inventer de nouveaux schémas.

Quoi qu'il en soit, comme l'a montré Steffen, votre schéma proposé pourrait fonctionner à nouveau avec un modèle d'attaque aussi étrange. J'utiliserais toujours hash(hash(domain + username) + password)plutôt que de hash(domain + username + password)simplement statuer sur la possibilité distante qui domain + username + passwordest encore un mot dans un dictionnaire.
Comme l'a montré mti2935, SRP est une alternative plus intéressante. L'authentification basée sur les certificats (c'est-à-dire celle gérée par le navigateur) est une autre option (que je trouve meilleure que de la faire manuellement dans un script JS potentiellement corrompu, comme vous semblez l'avoir proposé dans les commentaires).