Comment bcrypt résiste aux GPU
Pourquoi bcrypt fait chuter le débit de cassage de milliards à quelques milliers par seconde : facteur de coût, key schedule hostile au GPU, et 72 octets.
bcrypt est la réponse ennuyeuse et correcte au stockage des mots de passe, et la raison de sa justesse est aussi celle qui le fait détester des attaquants : il refuse d'aller vite.
Là où MD5 offre à un GPU des dizaines de milliards d'essais par seconde, une empreinte bcrypt bien réglée fait tomber ce chiffre à quelques milliers. Ce n'est pas un artefact de réglage. C'est tout le design, et il tient face au matériel qui rend les hash rapides inutiles.
Le facteur de coût est la molette
Une empreinte bcrypt porte ses propres paramètres. Une chaîne comme $2y$12$R9h... se décode en algorithme 2y, coût 12, puis un sel base64 de 22 caractères et l'empreinte. Ce 12 est le facteur de travail, et c'est un exposant en base 2 : le key schedule effectue 2^12 = 4096 itérations. Passez à 13 et vous doublez le travail. Descendez à 10 et vous le divisez par quatre.
C'est ce qui permet à bcrypt de vieillir avec grâce. Vous choisissez un coût aujourd'hui, et dans trois ans, quand le matériel sera plus rapide, vous l'augmentez de un ou deux pour rester en avance. Le format enregistre le coût avec lequel l'empreinte a été créée, donc anciennes et nouvelles coexistent et vous pouvez mettre à niveau chacune à la prochaine connexion de l'utilisateur.
Pourquoi Blowfish déteste les GPU
La lenteur ne tient pas qu'au nombre d'itérations. MD5 itéré 4096 fois se casserait encore plus vite que bcrypt, car les itérations ne sont pas toute l'histoire.
bcrypt repose sur un key schedule Blowfish modifié appelé EksBlowfish. Le chiffrement Blowfish dépend de grandes S-boxes, quatre tables de 256 entrées de 32 bits, lues et réécrites en permanence pendant la préparation de clé. Chaque round fait de petits accès mémoire dépendants des données dans un état qui ne tient pas proprement dans les registres rapides ou la mémoire partagée d'un GPU. Les GPU gagnent quand des milliers de threads exécutent la même instruction sur des données larges et prévisibles. bcrypt leur donne l'inverse : des accès mémoire dispersés et quasi sériels par essai, chaque thread se disputant la même mémoire embarquée rare.
Résultat : les milliers de cœurs d'un GPU ne peuvent pas tous rester occupés. Par cœur, bcrypt est à peu près aussi lent sur GPU que sur CPU, ce qui efface l'habituel avantage matériel d'un facteur 1000. C'est le contraste délibéré avec le danger des hash rapides : bcrypt est conçu pour être hostile précisément au parallélisme qui fait tomber MD5 et NTLM en quelques secondes.
Casser bcrypt en pratique
On attaque bcrypt avec le mode hashcat -m 3200.
hashcat -m 3200 -a 0 hashes.txt cible-restreinte.txt -r rules/best64.rule
Notez la wordlist. Contre MD5 vous lancez rockyou et un gros jeu de règles parce que vous pouvez vous offrir cent millions de candidats. Contre bcrypt à coût 12, non. À quelques milliers d'essais par seconde, rockyou seul est un run de plusieurs semaines avant même que les règles entrent en jeu. Vous apportez donc de toutes petites listes à fort signal : variantes du nom de l'entreprise, mots de passe corrélés aux fuites pour les utilisateurs visés, motifs issus de l'OSINT. Vous dépensez vos essais là où ils ont le plus de chances de tomber. C'est toute la discipline derrière les wordlists et règles qui marchent vraiment, et bcrypt est l'endroit où l'ignorer vous coûte la mission.
Si les mots de passe étaient réellement solides et le coût raisonnable, vous ne cassez pas bcrypt. Vous passez à autre chose et trouvez une autre voie d'entrée. C'est l'issue honnête, et c'est le but.
Le piège des 72 octets
bcrypt ne traite que les 72 premiers octets du mot de passe. Tout ce qui dépasse est ignoré en silence. Une phrase de passe de 200 caractères n'est, pour bcrypt, que ses 72 premiers octets. Cela compte rarement, mais ça mord quand une application pré-rembourre l'entrée ou concatène des champs.
Pire encore, le problème de l'octet nul. Certaines implémentations bcrypt s'appuient sur la gestion des chaînes en C et tronquent au premier \0. Donnez un mot de passe contenant un octet nul et tout ce qui suit disparaît, réduisant parfois la sécurité à presque rien.
Les deux écueils convergent vers un même mauvais motif : le pré-hachage. Les équipes qui veulent supporter de longs mots de passe lancent parfois SHA-256 d'abord, puis bcrypt sur l'empreinte. Raisonnable en principe, mais si vous encodez l'empreinte en base64 ou hexadécimal, tout va bien, alors que si vous passez les 32 octets binaires bruts, vous pouvez heurter un nul embarqué et tronquer. La recette sûre : pré-hacher en SHA-256, encoder le résultat en base64, puis bcrypt cela. Sachez ce que fait votre stack.
Ce que les défenseurs doivent vraiment faire
Choisissez un facteur de coût par mesure, pas par folklore. Visez environ 250 millisecondes par vérification sur votre matériel de production. Assez lent pour écraser un attaquant, assez rapide pour que les utilisateurs ne remarquent rien à la connexion. Sur les serveurs actuels cela tombe souvent autour de coût 12 à 14, mais mesurez vous-même, car un chiffre copié d'un billet de 2015 peut aujourd'hui être trivialement rapide.
Puis traitez le coût comme une chose à revisiter. Planifiez une revue, re-hachez à la connexion quand vous l'augmentez, et surveillez votre latence d'authentification pour qu'une hausse de coût ne devienne pas en douce un vecteur de déni de service. bcrypt n'a pas besoin d'être remplacé sur la plupart des systèmes. Il a besoin que vous continuiez à tourner la molette.