
The hmac-bcrypt password hashing function
Ce dépôt contient des implémentations de référence de la fonction de hachage de mots de passe hmac-bcrypt dans plusieurs langages. Chaque implémentation de référence tente d'être un portage 1:1 des implémentations originales en C et en Perl créées par @epixoip lorsque cela est possible, et elles sont entièrement compatibles entre elles (c'est-à-dire qu'elles produisent et valident les mêmes valeurs de hachage.)
Chaque implémentation de référence définit deux fonctions procédurales avec les pseudo-prototypes suivants :
string hmac_bcrypt_hash(password, settings?, pepper?)
boolean hmac_bcrypt_verify(password, expected, pepper?)
Veuillez vous référer aux cas de test fournis avec chaque implémentation de référence pour savoir comment intégrer et utiliser ces fonctions dans votre projet. Une interface procédurale a été choisie pour sa simplicité, mais vous êtes libre d'incorporer ces fonctions dans des classes ou des objets comme vous le souhaitez.
Le paramètre settings fait référence, dans ce contexte, à une chaîne de paramètres bcrypt standard contenant l'identifiant de hachage (2a), le coût log2 (par exemple 13) et une valeur de sel optionnelle de 22 octets encodée en radix64 (par exemple LhayLxezLhK1LhWvKxCyLO). Ces valeurs sont concaténées dans une chaîne délimitée par des dollars ; par exemple, $2a$13$LhayLxezLhK1LhWvKxCyLO.
Le paramètre settings est optionnel ; le plus souvent, il doit être laissé nul/vide pour utiliser le coût par défaut de 13 et un sel généré. Au plus, si vous souhaitez utiliser une valeur de coût autre que 13, vous pouvez fournir uniquement l'identifiant + la valeur de coût (par exemple $2a$10$). Il n'est pas recommandé de créer et de fournir vos propres valeurs de sel !
Le paramètre pepper définit un secret partagé global et est lui aussi optionnel ; s'il est nul/vide, la valeur par défaut de hmac_bcrypt est utilisée. Cela sert principalement à se défendre contre les attaques par shucking, mais peut également être utilisé pour augmenter la sécurité, la difficulté et le coût de craquage (surtout lorsqu'il est utilisé conjointement avec un HSM.)
La fonction de hachage de mots de passe hmac-bcrypt utilise bcrypt avec un pré-hachage et un post-hachage appropriés, combinés à un pepper optionnel. En pseudo-code, c'est assez simple :
pre_hash = hmac_sha512_base64(password, pepper)
mid_hash = bcrypt(pre_hash, settings)
post_hash = hmac_sha512_base64(mid_hash, pepper)
return settings + post_hash
Le pré-hachage est utilisé pour permettre des longueurs d'entrée supérieures au maximum de 72 octets de bcrypt. SHA-512 a été choisi en raison de sa taille de mot de 64 bits, qui est favorable aux défenseurs utilisant le CPU mais défavorable aux attaquants utilisant le GPU. Cependant, une valeur SHA-512 brute ne peut pas être utilisée pour plusieurs raisons :
Pour atténuer les attaques par shucking, le pré-hachage doit être salé — ou dans ce cas, poivré — et HMAC constitue un moyen pratique d'appliquer une clé à un hachage. La valeur HMAC résultante est ensuite encodée en base64 pour produire une entrée propre, en ASCII inférieur, qui atténue les problèmes liés aux octets nuls et aux données binaires.
Le lecteur attentif remarquera que hmac_sha512_base64 produit 88 octets de données, alors que bcrypt a une taille d'entrée maximale de 72 octets. Ce n'est pas un problème, et c'est même préférable à l'utilisation d'un algorithme de hachage qui produit moins de données d'entrée, comme sha256. Nous voulons remplir les 72 octets, et aucune sécurité n'est perdue en tronquant sha512 à 432 bits (ce qui est supérieur aux 384 bits fournis par sha384).
Le post-hachage est utilisé principalement pour différencier les hachages hmac-bcrypt des hachages bcrypt — c'est-à-dire que les longueurs différeront — mais aussi pour ajouter une couche de protection supplémentaire grâce au pepper. L'étape de post-hachage pourrait même être effectuée avec la valeur du pepper stockée dans un HSM (hautement recommandé !) pour une protection supplémentaire.
Bien que la dureté mémoire (memory-hardness) ait été une expérience intéressante, la bonne voie pour atteindre une résistance à l'accélération est assez clairement la dureté cache (cache-hardness). Les vitesses de mémoire et la bande passante continuent d'augmenter, tandis que la RAM devient plus grande, moins chère et plus dense. Mais les tailles de cache, les vitesses de cache et les coûts de cache sont relativement statiques. Même les instructions matérielles scatter/gather n'ont pas eu l'impact spectaculaire que nous avions prédit qu'elles pourraient avoir sur les algorithmes cache-hard.
Les meilleurs algorithmes memory-hard — Argon2 et scrypt — sont en réalité moins résistants à l'accélération que les algorithmes cache-hard pour un temps d'exécution cible inférieur à 1000ms, ce qui en fait d'excellents KDF mais pas d'excellents outils pour l'authentification en temps réel.
Idéalement, on utiliserait une fonction de hachage de mots de passe intentionnellement cache-hard, comme pufferfish ou bscrypt. Cependant, ces fonctions sont plus récentes, moins étudiées et disposent de peu de bibliothèques disponibles. bcrypt, en revanche — bien qu'involontairement cache-hard — est facilement disponible pour pratiquement tous les langages et frameworks.
Parmi les algorithmes dont nous disposons facilement, bcrypt offre la plus grande résistance à l'accélération pour l'authentification interactive en temps réel (temps d'exécution cible < 1000ms), la réponse évidente est donc d'exploiter le bcrypt dont nous disposons.
Cependant, bcrypt présente certaines limitations notables, comme ses détracteurs, très virulents, s'empressent de le faire remarquer :
hmac-bcrypt résout ces deux problèmes, et bien plus encore.