web-dev-qa-db-fra.com

Pourquoi improviser votre propre fonction de hachage à partir des fonctions de hachage existantes est si mauvais

J'ai bien peur de me faire jeter des tomates pour avoir posé cette vieille question, mais voilà.

Après avoir lu que préparer votre propre hachage de mot de passe à partir des fonctions de hachage existantes est dangereux over et over encore une fois, je ne comprends toujours pas la logique. Voici quelques exemples:

  • md5(md5(salt) + bcrypt(password))
  • scrypt(bcrypt(password + salt))
  • sha1(md5(scrypt(password + md5(salt))))

Les arguments typiques contre ceux-ci sont les suivants:

Vous n'êtes pas un cryptographe! Vous n'avez aucune idée si ces hachages sont plus sûrs. Laissez-le aux experts qui savent ce qu'ils font. Ceux-ci n'ajoutent aucune sécurité supplémentaire.

Certes, ils n'améliorent pas la fonction comme un hachage (c'est-à-dire qu'il est plus difficile d'inverser ou de trouver des collisions, etc.), mais sûrement sûrement ils ne le font pas pire comme un hachage? S'ils le faisaient, les pirates seraient en mesure de ré-hacher les mots de passe hachés standard en ces hachages farfelus comme ils l'entendent et affaibliraient le hachage? Je ne l'achète pas.

Deuxième argument:

Principe de Kerckoffs : Un cryptosystème doit être sécurisé même si tout sur le système est connu.

D'accord. C'est essentiellement la motivation pour ne pas stocker vos mots de passe sous forme de texte en clair en premier lieu. Mais si ma réponse à la première critique tient, alors ces hachages farfelus fonctionnent toujours comme des hachages sécurisés, et notre système ne rompt pas le principe de Kerckoffs plus qu'il ne le ferait avec un hachage standard.

Voici deux avantages possibles (et valables pour autant que je sache) d'utiliser un hachage "farfelu" par rapport à un hachage normal:

  1. Bien sûr, votre système doit être sécurisé si l'attaquant a le code source, mais il est très probable que votre attaquant n'ait pas accès à votre code source et probablement ne pourra pas deviner votre hachage farfelu, rendant impossible toute tentative de force brute.
  2. (Celui-ci est la vraie motivation derrière moi pour poser cette question) BCrypt est pensé pour être sécurisé, difficile pour le CPU et le GPU (excellent) mais peut être très rapide avec du matériel spécialisé . SCrypt serait difficile à renforcer brutalement sur les processeurs, les GPU et actuellement disponibles sur le marché, mais il est plus récent et peu fiable par la communauté cryptographique autant que BCrypt en raison du manque d'exposition qu'il a eu. Mais le hachage BCrypt(SCrypt(password + salt)) n'obtient-il pas le meilleur des deux mondes?

J'apprécie que la passion/colère derrière la plupart des diatribes contre ces hachages maison provient du manque de connaissance du programmeur moyen de ce qui fait un bon hachage, et d'un souci que d'encourager ce genre de hachage farfelu se retrouvera inévitablement avec faible et inutile hachages entrant dans le code de production. Mais Si le hachage farfelu est soigneusement construit à partir de hachages solides et fiables, les gains de sécurité ne sont-ils pas très précieux et réels?


Mise à jour

J'ai obtenu un tas de bonnes réponses à ce sujet, merci. Ce que je semblais oublier dans mes hypothèses était que, bien que la combinaison de hachages ne puisse pas faciliter le crack du mot de passe original et donc le crack des hachages constitutifs, la combinaison de deux hachages sécurisés ou plus peut - au moins en principe - être plus faible que n'importe lequel de ses hachages internes en raison des interactions non étudiées et complexes entre eux. Cela signifie qu'il pourrait être possible de trouver certains chaîne qui a dépassé le hachage farfelu sans nécessairement casser les hachages qui l'ont composé.

80
George Powell

Le fait que vous devez poser cette question est la réponse elle-même - vous ne savez pas ce qui ne va pas avec l'empilement de ces primitives, et donc ne peut pas savoir quels sont les avantages ou les faiblesses.

Faisons une analyse de chacun des exemples que vous avez donnés:

md5(md5(salt) + bcrypt(password))

Je peux voir quelques problèmes ici. La première, c'est que vous êtes MD5 en train de manger le sel. Quel avantage cela apporte-t-il? Aucun. Cela ajoute de la complexité et le sel est simplement destiné à être unique pour empêcher les collisions de mots de passe et les attaques de pré-calcul (par exemple, Rainbow table). L'utilisation de MD5 ici n'a aucun sens et pourrait en fait affaiblir le schéma car MD5 a connu des collisions triviales. En tant que tel, il y a une petite possibilité que l'introduction de MD5 ici puisse signifier que deux sels uniques produisent le même hachage MD5, résultant en un sel efficacement dupliqué. C'est mauvais.

Ensuite, vous utilisez bcrypt sur le mot de passe. D'accord. Eh bien, la plupart des implémentations de bcrypt nécessitent un sel en interne, ce qui est déjà techniquement invalide. Disons que vous le savez et que vous vouliez dire bcrypt(md5(salt), password). Cette partie tombe toujours à la faiblesse que j'ai décrite ci-dessus, mais elle n'est pas trop minable - retirez le MD5 et c'est une utilisation standard de bcrypt.

Enfin, vous MD5 le tout. Pourquoi fais-tu ça? Quel est le but? Quel avantage cela apporte-t-il? Pour autant que je puisse voir, il n'y a aucun avantage du tout. Au détriment, cela ajoute plus de complexité. Comme la plupart des implémentations bcrypt utilisent la notation $2a$rounds$salt$hash, Vous devrez écrire du code pour l'analyser afin de pouvoir extraire la partie de hachage et stocker le reste séparément. Vous aurez également besoin d'une implémentation MD5, ce qui n'était pas nécessaire.

Donc, en termes d'empreinte de code pour les vecteurs d'attaque potentiels, vous êtes passé d'une simple implémentation de bcrypt à une implémentation de bcrypt avec du code d'analyse personnalisé et une implémentation MD5, et du code de collage pour tout coller. Pour un bénéfice nul et une vulnérabilité potentielle dans la manipulation du sel.

Le prochain:

scrypt(bcrypt(password + salt))

Celui-ci n'est pas trop mal, mais encore une fois, vous avez besoin de code pour analyser séparément les résultats de bcrypt en hachage et sel/tour. Dans ce cas, je devrais deviner qu'il y a un léger avantage, car bcrypt et scrypt fonctionnent de différentes manières pour à peu près le même objectif, ce qui rendrait un peu plus difficile pour un attaquant extrêmement bien financé de créer des ASIC personnalisés pour briser votre système. Mais est-ce vraiment nécessaire? Allez-vous vraiment frapper une situation où un État-nation consacrera quelques millions de dollars juste pour casser votre hachage? Et, si ce cas se présente, cela va-t-il vraiment déranger l'attaquant d'avoir à dépenser quelques millions supplémentaires pour doubler leur nombre de puces?

Un autre problème potentiel avec la combinaison de bcrypt et scrypt comme celui-ci est qu'il y a eu très peu d'études sur la façon dont les deux interagissent. En tant que tel, nous ne savons pas s'il y a des cas étranges qui peuvent causer des problèmes. Comme exemple plus évident, prenez le pavé unique. Nous calculons c=m^k Pour certains messages m et certaines clés parfaitement aléatoires de longueur égale k, et nous obtenons une sécurité parfaite. Alors faisons-le deux fois , pour encore plus de sécurité! Cela nous donne c=m^k^k ... oh, attendez, cela nous donne juste m. Donc, parce que nous n'avons pas pris le temps de bien comprendre comment fonctionnaient les internes du système, nous nous sommes retrouvés avec une véritable vulnérabilité de sécurité. C'est évidemment plus compliqué dans le cas des KDF, mais le même principe s'applique.

Et enfin:

sha1(md5(scrypt(password + md5(salt))))

Encore une fois, nous rencontrons le problème du sel MD5. Je suis également intrigué par MD5 sur le hachage SHA1. Quel avantage cela pourrait-il avoir si vous utilisez déjà un KDF lent comme scrypt? Les quelques nanosecondes qu'il faudrait pour calculer ces hachages pâlissent par rapport aux centaines de millisecondes qu'il faudrait pour calculer le résumé de cryptage du mot de passe. Vous ajoutez de la complexité à une couche de "sécurité" absolument hors de propos, ce qui est toujours une mauvaise chose. Chaque ligne de code que vous écrivez est une vulnérabilité potentielle.


Maintenant, souvenez-vous de ce que j'ai dit au début de ma réponse. Si, à un moment donné de cette réponse, vous pensiez "oh ouais, je n'y ai pas pensé", alors mon argument est prouvé.

Vous rencontrez ce que je qualifierais de Dave fausse maxime:

Si j'ajoute plus de choses cryptographiques, ce sera plus sécurisé.

C'est un trait commun chez les développeurs, et je l'ai cru une fois aussi. Cela va de pair avec le déni d'autres principes, tels que Principe de Kerckhoff . En fin de compte, vous devez réaliser et accepter que l'obscurité n'est pas un rail de sécurité; c'est une béquille pour une crypto faible. Si votre crypto est solide, elle n'a pas besoin de béquille.

70
Polynomial

Les primitives de chiffrement peuvent être empilées en toute sécurité et augmenter la sécurité si, et seulement si, vous connaissez suffisamment les primitives pour comprendre leurs faiblesses et comment ces faiblesses interagissent. Si vous ne les connaissez pas ou ne comprenez pas les détails - eh bien, c'est comme ça que vous obtenez protocole de Dave .

Le problème est que très peu de gens les connaissent tous assez bien pour juger si une certaine combinaison est sûre. C'est pourquoi il doit être quelque chose qui est publié et révisé, s'il n'a pas été révisé, vous n'avez aucun moyen de savoir s'il est aussi fort que scrypt ou s'il est plus proche de CRC32.

Donc, si vous n'êtes pas un expert - il est fort possible que vous ayez quelque chose de plus faible que la primitive la plus faible que vous ayez utilisée (voir le protocole de Dave) et que vous ne le sachiez pas. Ou du moins, vous ne le sauriez pas avant qu'il ne soit fissuré - trouver les mots de passe de vos utilisateurs sur Pastebin n'est pas tout à fait le moyen idéal pour déterminer que le schéma est défectueux.

Je suis d'accord qu'un certain degré d'obscurité peut aider dans une perspective de défense en profondeur, mais le système sous-jacent doit être sécurisé.

Entre scrypt, bcrypt et PBDKF2 - au moins l'un d'entre eux sera pris en charge sur à peu près toutes les plateformes. Ceux-ci sont connus et bien testés - ils offrent différents niveaux de protection, mais ils sont encore beaucoup plus sûrs qu'un empilement bizarre de md5 et sha1.

15
Adam Caudill

Pour votre question spécifique de combiner scrypt et bcrypt, n'oubliez pas que ces fonctions ont un coût configurable et que vous souhaitez augmenter ce coût autant que possible, tout en le maintenant tolérable pour votre utilisation spécifique. Par exemple, si vous pouvez utiliser bcrypt avec jusqu'à [~ # ~] x [~ # ~] itérations (au-delà desquelles il est trop cher pour votre serveur et votre nombre moyen de connexions utilisateur par seconde), ou chiffrez avec [~ # ~] y [~ # ~] itérations, alors vous ne peut pas utiliser scrypt (bcrypt) avec [~ # ~] x [~ # ~] itérations pour bcrypt then [~ # ~] y [~ # ~] itérations pour scrypt: cela dépassera votre budget CPU.

Ainsi, si vous scryptez et bcrypt en cascade, vous devez utiliser les deux avec moins d'itérations que ce que vous auriez pu faire avec une seule. Vous n'obtenez pas le meilleur des deux mondes en les enchaînant simplement. En fait, le mieux que vous puissiez espérer est une sorte de moyenne entre les deux. Et cela se fait au prix d'un code plus complexe, ce qui est intrinsèquement mauvais quand on parle de sécurité (ou, d'ailleurs, de maintenabilité).

13
Thomas Pornin

En plus de la réponse d'Adam, je voudrais également mentionner que chaque fois que vous utilisez la cryptographie, vous devriez avoir une raison forte et inévitable de le faire. Dans vos exemples ci-dessus, cela n'existe pas.

md5(md5(salt) + bcrypt(password))
scrypt(bcrypt(password + salt))

Les algorithmes bcrypt et scrypt sont déjà assez forts et considérés comme incassables. Quel problème essayez-vous de résoudre? Et pourquoi pensez-vous que la combinaison de leurs résultats (en particulier avec md5) va le résoudre? Dans le meilleur des cas, vous avez probablement simplement réduit la difficulté de déchiffrer le mot de passe à celui du hachage le plus faible, plutôt que d'améliorer réellement la sécurité. Et le pire des cas est terriblement indéfini.

md5(sha1(md5(md5(password) + sha1(password + salt)) + password))

Cette solution est encore pire. Il implémente manuellement un schéma de hachage répété, mais sans suffisamment de tours pour imposer un facteur de travail significatif aux attaquants.

En résumé, le problème est que:

  • vous jetez autour de la cryptographie sans avoir réellement un problème qui doit être résolu
  • vous avez considérablement augmenté la probabilité d'introduire des failles dans votre implémentation
  • vous avez probablement réduit la sécurité au plus faible des algorithmes de hachage, et
  • vous avez introduit un pire scénario inconnu où aucun n'existait
9
Stephen Touset

Si vous appliquez des opérations dangereuses à un algorithme sécurisé, vous pouvez définitivement interrompre la fonction de hachage. Votre nouvelle fonction pourrait même être bien pire que le maillon le plus faible.

Pourquoi les attaquants ne l'utilisent-ils pas pour casser des fonctions sécurisées? Ça ne les aide pas. Par exemple, si j'écrase les 440 premiers bits d'un mot de passe stocké en toute sécurité à l'aide de bcrypt avec des zéros, je peux facilement trouver un mot de passe correspondant par force brute, mais ce mot de passe ne fonctionnera que sur mon propre algorithme terrible. Une mise en œuvre sensée la rejetterait probablement.

La remise à zéro de gros morceaux de hachage est clairement mauvaise, mais même des opérations sûres peuvent être combinées en quelque chose de dangereux. Ajouter deux nombres (modulo n pour assurer une longueur constante) est "sûr". Généralement, aucune entropie n'est perdue. Pourtant, h (x) + h(x) mod n réduit la qualité du hachage h (x) d'un bit, car le résultat est toujours pair. L'opérateur tout aussi sûr XOR fait encore pire, comme h (x) XOR h (x) = renvoie toujours zéro.

Ces écueils sont assez évidents, mais pas tous. Gardez à l'esprit que, comme toujours, il est trivial d'inventer un schéma suffisamment bon pour que vous ne trouviez pas de faiblesses vous-même, mais très difficile d'en inventer un là où personne d'autre ne le peut.

8
Marcks Thomas

Les fonctions de hachage sont construites par des cryptographes et détruites par des cryptographes. Il existe de nombreuses fonctions de hachage fortes ainsi que des fonctions faibles encore utilisées aujourd'hui. Les programmeurs doivent faire confiance aux cryptographes et à la fonction de hachage. S'il y avait jamais une vulnérabilité dans la fonction de hachage, alors vous en entendriez sûrement parler sur Internet ou par l'intermédiaire de collègues, puis les cryptographes feront sûrement une enquête approfondie. Avec tout algorithme de hachage sécurisé, seule la faiblesse connue peut être une attaque par bruteforce.

La combinaison des fonctions de hachage n'ajoute presque aucune sécurité supplémentaire et tout ce que vous voudrez peut-être est déjà déjà implémenté dans la fonction.

Saler un mot de passe est idéal pour réduire l'efficacité des tables Rainbow, de sorte qu'un mot de passe ne peut pas être simplement "recherché". Que vous hachiez une fonction deux fois ou que vous changiez la fonction de hachage, il s'agit essentiellement de saler le mot de passe. Et la plupart des fonctions incluent une méthode facile à saler, il n'est donc pas vraiment nécessaire de l'implémenter.

Disons que je veux créer mon propre hachage sécurisé parce que tout le monde le fait. Et comme je ne suis pas un cryptographe, j'en aurai besoin "vraiment" en toute sécurité, car bien sûr, chaque programmeur sait comment créer un hachage sécurisé au lieu d'utiliser ceux déjà créés. Je crée donc ma fonction de hachage sournoise, mod10 (md5 (sha1 (bcrypt (mot de passe + sel))))).

Comme vous pouvez le voir sur ma fonction de hachage, il est vraiment sécurisé car j'utilise tellement de choses différentes. Bien sûr, dans cet exemple stupide, il est facile de voir qu'il n'y aura que 10 sorties différentes possibles. Mais en utilisant simplement une seule fonction de hachage sécurisée, cela aurait complètement évité cela.

Bien sûr, votre système devrait être sécurisé si l'attaquant possède le code source, mais il est très probable que votre attaquant n'ait pas accès à votre code source et ne sera probablement pas en mesure de deviner votre hachage farfelu, en tentant une brute force impossible

Nous supposons donc qu'un attaquant s'est emparé de la table de base de données qui contient les hachages. Je suppose qu'il serait très probable qu'un attaquant puisse également obtenir les fichiers de la page Web. Vos systèmes exécutant ces services peuvent être le même exploit qui vous a permis de récupérer la base de données. Un serveur de base de données est configuré pour que ce public ne puisse pas y accéder directement. En revanche, votre serveur web contenant votre code est en première ligne.

6
ponsfonze

Chaque fois que vous augmentez la complexité d'un algorithme ou ajoutez même plus de lignes de code, vous augmentez les points de défaillance de votre application. La combinaison d'algorithmes peut avoir des conséquences imprévues. Cela peut conduire à certains raconte ou d'autres signes qui peuvent en fait affaiblir la force cryptographique du système.

Plus vous utilisez de bibliothèques dans votre application, plus le risque est grand pour votre application en général. Si une faille est trouvée dans une implémentation qui permet une faiblesse, l'exécution de code, etc., votre application est vulnérable. Si par hasard, vous avez sélectionné un autre algorithme qui n'a pas été attaqué, votre coffre-fort pour le moment (bien sûr, vous pourriez également être du côté malchanceux de la chance).

Rappelez-vous [~ # ~] baiser [~ # ~] : Restez simple, stupide , sinon vous risquez de vous perdre dans la complexité.

5
Eric G

Je vais être en désaccord avec tout un tas de gens qui sont plus intelligents que moi et plus expérimentés en sécurité que moi. Je me trompe donc probablement.

Improviser votre propre fonction de hachage est une bonne idée - si vous le faites correctement. Suivez ces 3 étapes simples.

  1. Identifiez une faiblesse ou un défaut dans les fonctions de hachage existantes.
  2. Improvisez votre propre fonction de hachage qui n'a pas ce défaut.
  3. Vérifiez que votre fonction de hachage improvisée possède toutes les forces des fonctions de hachage existantes.

Après avoir terminé l'étape 3, vous seriez fou de ne pas utiliser votre fonction de hachage improvisée.

2
emory

Lors de la concaténation de différentes fonctions de hachage parce que vous craignez celle, il est essentiel que vous appliquiez le sel avant d'appliquer le prochain algorithme de hachage, alors toute faiblesse de collision dans l'un des algorithmes ne corrompra pas la deuxième fonction de hachage que vous utilisez:

scrypt (bcrypt (mot de passe + sel) + sel)

Mais je pense que scrypt est une technique établie maintenant, Argon 2i a remporté le concours de hachage de mot de passe et est censé être plus sécurisé et bcrypt existe depuis longtemps et s'est avéré être sécurisé contre les contournements triviaux.

Par conséquent, les éléments suivants auraient plus de sens et combineraient la force de l'argon 2i, mais se replieraient sur bcrypt si un futur attaquant montrait comment briser trivialement l'argon 2i, qui est actuellement considéré comme difficile:

bcrypt (Argon2i (mot de passe + sel) + sel)

Mais vous risquez moins de faire une erreur si vous faites simplement:

scrypt (mot de passe + sel) Ou bcrypt (mot de passe + sel)

Rappelez-vous, la plupart des violations sont dues à une erreur humaine dans le code, beaucoup mieux pour rester simple et examiner votre code en profondeur avec une analyse statique et dynamique et des réviseurs de code humain, pour vous assurer qu'aucune attaque d'injection SQL ne se déroule (n'oubliez pas de paramétrer toujours vos requêtes de base de données !)

0
user3083447