Le problème ici est essentiellement un problème d'entropie. Alors commençons à y regarder:
Entropie par caractère
Le nombre de bits d'entropie par octet est:
- Caractères hexagonaux
- 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, comment 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 hash_hmac()
n'est pas défini).
Par conséquent, en hachant votre poivre, vous coupez efficacement l'entropie maximale disponible pour le mot de passe par un facteur de 2 (de 576 à 288 bits possibles ).
Le deuxième problème
Cependant, sha256
ne fournit que des 256
bits d'entropie en premier lieu. Vous réduisez donc efficacement 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 partiellement résoudre 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 qui le réduit de ...
Le problème sous-jacent
Le problème sous-jacent est qu'il existe trois types de mots de passe de plus de 72 caractères. L'impact de ce système de style sur eux sera très différent:
Remarque: à partir de maintenant, je suppose que nous comparons à un système de poivre qui utilise SHA512
une sortie brute (non 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 quel montant en grandes 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 caractère. 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 considérablement leur entropie disponible dans bcrypt
.
Permettez-moi de le répéter. Pour les utilisateurs qui utilisent des mots de passe longs et à forte entropie, votre solution réduit considérablement 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 des mots de passe plus longs)
Mots de passe aléatoires à entropie moyenne
Ce groupe utilise des mots de passe contenant des symboles courants, mais pas d'octets de poids fort ni de caractères de contrôle. Ce sont vos mots de passe à saisir.
Pour ce groupe, vous allez débloquer légèrement plus d'entropie (ne pas le créer, mais permettre à plus d'entropie de s'insérer 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 de 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 considérablement plus d'entropie (pas le créer, mais permettre à plus de tenir dans l'entrée du mot de passe bcrypt) par hachage. Le seuil de rentabilité est d'environ 223 caractères avant de manquer d'entropie.
Disons cela encore. Pour cette classe de mots de passe, le pré-hachage augmente nettement la sécurité.
Retour au monde réel
Ces types de calculs d'entropie n'ont pas vraiment beaucoup 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 que peu de recherches aient été menées pour deviner l'entropie, je voudrais souligner certains points.
Les chances de deviner au hasard 72 caractères corrects d'affilée sont extrêmement faibles. Vous êtes plus susceptible de gagner à la loterie Powerball 21 fois 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 bien plus élevée que pour un mot de passe aléatoire. Mais c'est toujours trivialement bas (vous êtes plus susceptible de gagner à la loterie Powerball 5 fois, sur la base de 2,3 bits par caractère).
Pratiquement
Pratiquement, cela n'a pas vraiment d'importance. Les chances que quelqu'un devine correctement les 72 premiers caractères, là où ces derniers font une différence significative, sont si faibles qu'il ne vaut pas la peine de s'inquiéter. Pourquoi?
Eh bien, disons que vous prenez une phrase. Si la personne parvient à bien comprendre les 72 premiers caractères, elle est soit vraiment chanceuse (peu probable), soit c'est une phrase courante. S'il s'agit d'une phrase courante, la seule variable est la durée de la création.
Prenons un exemple. Prenons une citation de la Bible (simplement parce que c'est une source courante de texte long, pas pour une autre raison):
Tu ne convoiteras pas la maison de ton prochain. Vous ne convoitez pas la femme de votre voisin, ni son serviteur ou sa servante, ni son bœuf ou son âne, ni tout ce qui appartient à votre prochain.
Cela fait 180 caractères. Le 73e caractère est g
le deuxième neighbor's
. Si vous avez deviné cela, vous ne vous arrêterez probablement pas nei
, mais continuez 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 l'utilisation d'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. Vous pouvez certainement aider certains groupes. Certains peuvent certainement blesser.
Mais en fin de compte, rien de tout cela n'est trop important. Les chiffres que nous traitons sont juste MANIÈRE trop élevé. La différence d'entropie ne sera pas beaucoup.
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 ou le dernier à faire cette erreur) que l'attaque que vous essayez d'empêcher va se produire.
Concentrez-vous sur la sécurisation du reste du site. Et ajoutez un compteur d'entropie de mot de passe à la boîte de mot de passe lors de l'enregistrement 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 de 0,02 $) ...
En ce qui concerne l'utilisation d'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 l'introduction d'un hachage «poivré» dans bcrypt causera un jour des vulnérabilités inconnues (nous savons que cela hash1(hash2($value))
peut exposer des vulnérabilités importantes autour de la résistance aux collisions et des 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, insérez l'intégralité de la sortie de hachage dans un algorithme de chiffrement fort. Puis stockez le résultat chiffré.
Désormais, une attaque SQL-Injection ne divulguera 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 simple hachage (ce qui est prouvable, quelque chose avec le poivre "pré-hachage" ne fournit pas).
Remarque: si vous choisissez de faire cela, utilisez une bibliothèque. Pour PHP, je recommande fortement le Zend\Crypt
package de Zend Framework 2 . C'est en fait le seul que je recommande à l'heure actuelle. Il a été fortement revu, et il 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 avantageux parce que vous utilisez tous les algorithmes de manière bien comprise et bien étudiée (au moins relativement). Rappelles toi:
N'importe qui, de l'amateur le plus ignorant au meilleur cryptographe, peut créer un algorithme qu'il ne peut pas casser lui-même.