web-dev-qa-db-fra.com

Comment hacher des mots de passe longs (> 72 caractères) avec Blowfish

La semaine dernière, j'ai lu beaucoup d'articles sur le hachage de mot de passe et Blowfish semble être (l'un des) le meilleur algorithme de hachage en ce moment - mais ce n'est pas le sujet de cette question!

La limite de 72 caractères

Blowfish ne considère que les 72 premiers caractères du mot de passe saisi:

<?php
$password = "Wow. This is a super secret and super, super long password. Let's add some special ch4r4ct3rs a#d everything is fine :)";
$hash = password_hash($password, PASSWORD_BCRYPT);
var_dump($password);

$input = substr($password, 0, 72);
var_dump($input);

var_dump(password_verify($input, $hash));
?>

La sortie est:

string(119) "Wow. This is a super secret and super, super long password. Let's add some special ch4r4ct3rs a#d everything is fine :)"
string(72) "Wow. This is a super secret and super, super long password. Let's add so"
bool(true)

Comme vous pouvez le voir, seuls les 72 premiers caractères comptent. Twitter utilise Blowfish aka bcrypt pour stocker leurs mots de passe ( https://shouldichangemypassword.com/Twitter-hacked.php ) et devinez quoi: changez votre mot de passe Twitter en un mot de passe long avec plus de 72 caractères et vous pouvez vous connecter à votre compte en entrant uniquement les 72 premiers caractères.

Blowfish et poivre

Il y a beaucoup d'opinions différentes sur les mots de passe de "poivrage". Certaines personnes disent que ce n'est pas nécessaire, car vous devez supposer que la chaîne secrète de poivre est également connue/publiée afin qu'elle n'améliore pas le hachage. J'ai un serveur de base de données séparé, il est donc tout à fait possible que seule la base de données soit divulguée et non le piment constant.

Dans ce cas (poivre non divulgué) vous rendez plus difficile une attaque basée sur un dictionnaire (corrigez-moi si ce n'est pas correct). Si votre ficelle de poivre fuit également: pas si mal - vous avez toujours le sel et il est aussi bien protégé qu'un hachis sans poivre.

Je pense donc que poivrer le mot de passe n'est au moins pas un mauvais choix.

Suggestion

Ma suggestion pour obtenir un hachage Blowfish pour un mot de passe avec plus de 72 caractères (et poivre) est:

<?php
$pepper = "foIwUVmkKGrGucNJMOkxkvcQ79iPNzP5OKlbIdGPCMTjJcDYnR";

// Generate Hash
$password = "Wow. This is a super secret and super, super long password. Let's add some special ch4r4ct3rs a#d everything is fine :)";
$password_peppered = hash_hmac('sha256', $password, $pepper);
$hash = password_hash($password_peppered, PASSWORD_BCRYPT);

// Check
$input = substr($password, 0, 72);
$input_peppered = hash_hmac('sha256', $input, $pepper);

var_dump(password_verify($input_peppered, $hash));
?>

Ceci est basé sur cette question : password_verify return false.

La question

Quelle est la manière la plus sûre? Obtenir un hachage SHA-256 en premier (qui renvoie 64 caractères) ou considérer uniquement les 72 premiers caractères du mot de passe?

Avantages

  • L'utilisateur ne peut pas se connecter en entrant seulement les 72 premiers caractères
  • Vous pouvez ajouter le poivre sans dépasser la limite de caractères
  • La sortie de hash_hmac aurait probablement plus d'entropie que le mot de passe lui-même
  • Le mot de passe est haché par deux fonctions différentes

Les inconvénients

  • Seuls 64 caractères sont utilisés pour créer le hachage Blowfish


Edit 1: Cette question ne concerne que l'intégration PHP de blowfish/bcrypt. Merci pour les commentaires!

89
Frederik Kammer

Le problème ici est essentiellement un problème d'entropie. Commençons donc à chercher là-bas:

Entropie par personnage

Le nombre de bits d'entropie par octet est:

  • Caractères hexadécimaux
    • Bits: 4
    • Valeurs: 16
    • Entropie en 72 caractères: 288 bits
  • Alpha-numérique
    • Bits: 6
    • Valeurs: 62
    • Entropie en 72 caractères: 432 bits
  • Symboles "communs"
    • Bits: 6,5
    • Valeurs: 94
    • Entropie en 72 caractères: 468 bits
  • octets complets
    • Bits: 8
    • Valeurs: 255
    • Entropie en 72 caractères: 576 bits

Donc, la façon dont nous agissons dépend du type de personnages que nous attendons.

Le premier problème

Le premier problème avec votre code est que votre étape de hachage "pepper" génère des caractères hexadécimaux (puisque le quatrième paramètre de hash_hmac() n'est pas défini).

Par conséquent, en hachant votre poivre, vous réduisez efficacement l'entropie maximale disponible pour le mot de passe d'un facteur 2 (de 576 à 288 possible bits).

Le deuxième problème

Cependant, sha256 Ne fournit en premier lieu que 256 Bits d'entropie. Donc, vous réduisez efficacement un possible 576 bits à 256 bits. Votre étape de hachage * immédiatement *, par définition, perd au moins 50% de l'entropie possible dans le mot de passe.

Vous pouvez résoudre partiellement ce problème en passant à SHA512, Où vous ne réduisez l'entropie disponible que d'environ 12%. Mais c'est toujours une différence non négligeable. Ces 12% réduisent le nombre de permutations d'un facteur de 1.8e19. C'est un grand nombre ... Et c'est le facteur il le réduit de ...

La question sous-jacente

Le problème sous-jacent est qu'il existe trois types de mots de passe de plus de 72 caractères. L'impact que ce système de style a sur eux sera très différent:

Remarque: à partir de maintenant, je suppose que nous comparons à un système de poivre qui utilise SHA512 Avec une sortie brute (pas hexadécimale).

  • Mots de passe aléatoires à haute entropie

    Ce sont vos utilisateurs qui utilisent des générateurs de mots de passe qui génèrent de grosses clés pour les mots de passe. Ils sont aléatoires (générés, non choisis par l'homme) et ont une entropie élevée par personnage. Ces types utilisent des octets élevés (caractères> 127) et certains caractères de contrôle.

    Pour ce groupe, votre fonction de hachage réduira de manière significative leur entropie disponible en bcrypt.

    Permettez-moi de le répéter. Pour les utilisateurs qui utilisent une entropie élevée et des mots de passe longs, votre solution de manière significative réduit la force de leur mot de passe d'une quantité mesurable. (62 bits d'entropie perdus pour un mot de passe de 72 caractères, et plus pour les mots de passe plus longs)

  • Mots de passe aléatoires à entropie moyenne

    Ce groupe utilise des mots de passe contenant des symboles communs, mais pas d'octets élevés ou de caractères de contrôle. Ce sont vos mots de passe saisissables.

    Pour ce groupe, vous allez légèrement déverrouiller plus d'entropie (pas la créer, mais autoriser plus d'entropie à tenir dans le mot de passe bcrypt). Quand je dis légèrement, je veux dire légèrement. Le seuil de rentabilité se produit lorsque vous maximisez les 512 bits du SHA512. Par conséquent, le pic est à 78 caractères.

    Permettez-moi de le répéter. Pour cette classe de mots de passe, vous ne pouvez stocker que 6 caractères supplémentaires avant de manquer d'entropie.

  • Mots de passe non aléatoires à faible entropie

    Il s'agit du groupe qui utilise des caractères alphanumériques qui ne sont probablement pas générés aléatoirement. Quelque chose comme une citation biblique ou autre. Ces phrases ont environ 2,3 bits d'entropie par caractère.

    Pour ce groupe, vous pouvez déverrouiller de manière significative plus d'entropie (pas la créer, mais permettre à davantage de s'insérer dans l'entrée de mot de passe bcrypt) par hachage. Le seuil de rentabilité est d'environ 223 caractères avant de manquer d'entropie.

    Disons-le encore. Pour cette classe de mots de passe, le pré-hachage augmente considérablement la sécurité.

Retour au monde réel

Ces types de calculs d'entropie n'ont pas vraiment d'importance dans le monde réel. Ce qui compte, c'est de deviner l'entropie. C'est ce qui affecte directement ce que les attaquants peuvent faire. C'est ce que vous voulez maximiser.

Bien qu'il y ait peu de recherches sur la conjecture de l'entropie, il y a certains points que j'aimerais souligner.

Les chances de deviner au hasard 72 caractères corrects d'affilée sont extrêmement très faibles. Vous avez plus de chances de gagner 21 fois à la loterie Powerball que d'avoir cette collision ... C'est le nombre dont nous parlons.

Mais nous ne pouvons pas tomber dessus statistiquement. Dans le cas de phrases, la probabilité que les 72 premiers caractères soient identiques est beaucoup plus élevée que pour un mot de passe aléatoire. Mais il est encore trivialement bas (vous avez plus de chances de gagner la loterie Powerball 5 fois, sur la base de 2,3 bits par caractère).

Pratiquement

En pratique, cela n'a pas vraiment d'importance. Les chances que quelqu'un devine correctement les 72 premiers caractères, où ces derniers font une différence significative, sont si faibles que cela ne vaut pas la peine de s'inquiéter. Pourquoi?

Eh bien, disons que vous prenez une phrase. Si la personne peut obtenir les 72 premiers caractères correctement, elle est soit vraiment chanceuse (peu probable), soit c'est une expression courante. S'il s'agit d'une expression courante, la seule variable est la durée de sa création.

Prenons un exemple. Prenons une citation de la Bible (juste parce que c'est une source courante de texte long, pas pour toute autre raison):

Tu ne convoiteras pas la maison de ton prochain. Tu ne convoiteras pas la femme de ton prochain, ni son serviteur ou servante, ni son bœuf, ni son âne, ni rien de ce qui appartient à ton prochain.

Cela fait 180 caractères. Le 73e caractère est le g dans le second neighbor's. Si vous avez deviné cela, vous ne vous arrêterez probablement pas à nei, mais continuerez avec le reste du verset (puisque c'est ainsi que le mot de passe est susceptible d'être utilisé). Par conséquent, votre "hachage" n'a pas ajouté grand-chose.

BTW: Je ne préconise absolument PAS d'utiliser une citation biblique. En fait, c'est exactement le contraire.

Conclusion

Vous n'allez pas vraiment aider les gens qui utilisent des mots de passe longs en hachant d'abord. Certains groupes, vous pouvez certainement aider. Certains peuvent certainement vous blesser.

Mais au final, rien de tout cela n'est trop significatif. Les nombres auxquels nous avons affaire sont juste [~ # ~] façon [~ # ~] trop élevés. La différence d'entropie ne va pas être grande.

Vous feriez mieux de laisser bcrypt tel quel. Vous êtes plus susceptible de bousiller le hachage (littéralement, vous l'avez déjà fait, et vous n'êtes pas le premier, ni le dernier à faire cette erreur) que l'attaque que vous essayez de prévenir va se produire.

Concentrez-vous sur la sécurisation du reste du site. Et ajoutez un compteur d'entropie de mot de passe dans la zone de mot de passe lors de l'inscription pour indiquer la force du mot de passe (et indiquer si un mot de passe est trop long que l'utilisateur peut souhaiter le changer) ...

C'est mon 0,02 $ au moins (ou peut-être bien plus que 0,02 $) ...

Autant que d'utiliser un poivre "secret":

Il n'y a littéralement aucune recherche sur l'alimentation d'une fonction de hachage dans bcrypt. Par conséquent, il n'est pas clair au mieux si le fait d'introduire un hachage "parsemé" dans bcrypt causera des vulnérabilités inconnues (nous savons que faire hash1(hash2($value)) peut exposer des vulnérabilités importantes concernant la résistance aux collisions et les attaques de pré-image).

Étant donné que vous envisagez déjà de stocker une clé secrète (le "poivre"), pourquoi ne pas l'utiliser d'une manière bien étudiée et comprise? Pourquoi ne pas crypter le hachage avant de le stocker?

Fondamentalement, après avoir haché le mot de passe, alimentez l'intégralité de la sortie de hachage dans un algorithme de cryptage puissant. Enregistrez ensuite le résultat chiffré.

Maintenant, une attaque SQL-Injection ne fuira rien d'utile, car ils n'ont pas la clé de chiffrement. Et si la clé est divulguée, les attaquants ne sont pas mieux lotis que si vous utilisiez un hachage simple (ce qui est prouvable, quelque chose avec le poivre "pré-hachage" ne fournit pas).

Remarque: si vous choisissez de le faire, utilisez une bibliothèque. Pour PHP, je recommande fortement le package Zend\Crypt De Zend Framework 2. C'est en fait le seul que je recommanderais à l'heure actuelle. Il a été fortement révisé et prend toutes les décisions pour vous (ce qui est une très bonne chose) ...

Quelque chose comme:

use Zend\Crypt\BlockCipher;

public function createHash($password) {
    $hash = password_hash($password, PASSWORD_BCRYPT, ["cost"=>$this->cost]);

    $blockCipher = BlockCipher::factory('mcrypt', array('algo' => 'aes'));
    $blockCipher->setKey($this->key);
    return $blockCipher->encrypt($hash);
}

public function verifyHash($password, $hash) {
    $blockCipher = BlockCipher::factory('mcrypt', array('algo' => 'aes'));
    $blockCipher->setKey($this->key);
    $hash = $blockCipher->decrypt($hash);

    return password_verify($password, $hash);
}

Et c'est bénéfique parce que vous utilisez tous les algorithmes de manière bien comprise et bien étudiée (relativement au moins). Rappelles toi:

N'importe qui, de l'amateur le plus ignorant au meilleur cryptographe, peut créer un algorithme qu'il ne peut lui-même casser.

132
ircmaxell

Pepper les mots de passe est sûrement une bonne chose à faire, mais voyons pourquoi.

Nous devons d'abord répondre à la question quand exactement un poivre aide. Le poivre ne protège que les mots de passe, tant qu'il reste secret, donc si un attaquant a accès au serveur lui-même, cela ne sert à rien. Une attaque beaucoup plus facile est l'injection SQL, qui permet un accès en lecture à la base de données (à nos valeurs de hachage), j'ai préparé une démonstration de l'injection SQL pour montrer à quel point cela peut être facile (cliquez sur la flèche suivante pour obtenir une entrée préparée).

Alors qu'est-ce que le poivre aide réellement? Tant que le piment reste secret, il protège les mots de passe faibles d'une attaque par dictionnaire. Le mot de passe 1234 deviendrait alors quelque chose comme 1234-p*deDIUZeRweretWy+.O. Ce mot de passe n'est pas seulement beaucoup plus long, il contient également des caractères spéciaux et ne fera jamais partie d'un dictionnaire.

Maintenant, nous pouvons estimer les mots de passe que nos utilisateurs utiliseront, probablement plus d'utilisateurs entreront des mots de passe faibles, car il y a des utilisateurs avec des mots de passe entre 64 et 72 caractères (en fait, ce sera très rare).

Un autre point est la plage de forçage brut. La fonction de hachage sha256 retournera une sortie 256 bits ou des combinaisons 1.2E77, c'est beaucoup trop pour le forçage brutal, même pour les GPU (si je calcule correctement, cela nécessiterait environ 2E61 ans sur un GPU en 2013 ). On n'a donc pas vraiment de désavantage à appliquer le poivre. Étant donné que les valeurs de hachage ne sont pas systématiques, vous ne pouvez pas accélérer le forçage brut avec des modèles courants.

P.S. Pour autant que je sache, la limite de 72 caractères est spécifique à l'algorithme de BCrypt lui-même. La meilleure réponse que j'ai trouvée est this .

P.P.S Je pense que votre exemple est imparfait, vous ne pouvez pas générer le hachage avec la longueur complète du mot de passe et le vérifier avec un tronqué. Vous vouliez probablement appliquer le poivre de la même manière pour générer le hachage et pour vérifier le hachage.

5
martinstoeckli

Bcrypt utilise un algorithme basé sur l'algorithme de configuration de clé Blowfish coûteux.

La limite de mot de passe recommandée de 56 octets (y compris l'octet de terminaison nul) pour bcrypt se rapporte à la limite de 448 bits de la clé Blowfish. Les octets au-delà de cette limite ne sont pas entièrement mélangés dans le hachage résultant. La limite absolue de 72 octets sur les mots de passe bcrypt est donc moins pertinente, si l'on considère l'effet réel sur le hachage résultant de ces octets.

Si vous pensez que vos utilisateurs choisiraient normalement des mots de passe de plus de 55 octets, rappelez-vous que vous pouvez toujours augmenter les cycles d'étirement des mots de passe à la place, pour augmenter la sécurité en cas de violation de la table des mots de passe (bien que cela doive être beaucoup comparé à l'ajout de personnages). Si les droits d'accès des utilisateurs sont si importants que les utilisateurs auraient normalement besoin d'un mot de passe extrêmement long, l'expiration du mot de passe devrait également être courte, comme 2 semaines. Cela signifie qu'un mot de passe a beaucoup moins de chances de rester valide pendant qu'un pirate investit ses ressources pour éliminer le facteur de travail impliqué dans le test de chaque mot de passe d'essai pour voir s'il produira un hachage correspondant.

Bien sûr, dans le cas où la table des mots de passe n'est pas violée, nous ne devons autoriser que les pirates, au maximum, dix tentatives pour deviner le mot de passe de 55 octets d'un utilisateur, avant de verrouiller le compte de l'utilisateur;)

Si vous décidez de pré-hacher un mot de passe supérieur à 55 octets, vous devez utiliser SHA-384, car il a la plus grande sortie sans dépasser la limite.

2
Phil