05 - Hashing de mots de passe : bcrypt, argon2 et pourquoi pas SHA-256
Ce que tu vas apprendre
- Pourquoi on hash les mots de passe (et jamais les chiffrer)
- Pourquoi SHA-256 et MD5 sont de mauvais choix
- bcrypt, argon2, scrypt : lequel choisir et comment configurer le coût
Prerequisites
Comprendre la différence entre hashing et chiffrement est un plus. Voir la serie sur les clés et le chiffrement.
Regle numero un : jamais en clair
Si tu ne retiens qu'une seule chose de cet article : ne stocke jamais un mot de passe en clair dans ta base de donnees. Jamais. Pas en dev. Pas "temporairement". Pas dans une table users_backup. Jamais.
Pourquoi ? Parce que les bases de donnees se font voler. Ca arrive aux petites boîtes, ca arrive aux grosses. En 2024, RockYou2024 a expose 10 milliards de mots de passe. Si tes mots de passe sont en clair, c'est game over pour tes utilisateurs. Comme la plupart des gens reutilisent leurs mots de passe (oui, c'est mal, mais c'est la réalité), un leak chez toi compromet leurs comptes partout.
Hashing vs chiffrement
Le chiffrement est reversible : tu chiffres avec une clé, tu dechiffres avec la meme clé (ou une autre, en asymetrique). Le hashing est a sens unique : tu transformes le mot de passe en une empreinte fixe, et il est mathematiquement impossible de retrouver le mot de passe original à partir du hash.
Pour les mots de passe, on veut du hashing. Tu n'as jamais besoin de "retrouver" le mot de passe d'un utilisateur. Quand il se connecte, tu hash ce qu'il envoie et tu compares avec le hash stocke.
Pourquoi pas SHA-256 ?
SHA-256 est un algorithme de hashing. Alors pourquoi je te dis de ne pas l'utiliser pour les mots de passe ?
Parce qu'il est trop rapide.
Un GPU moderne (genre une RTX 4090) peut calculer environ 22 milliards de hash SHA-256 par seconde. Vingt-deux milliards. Tu as un mot de passe de 8 caractères avec lettres et chiffres ? L'espace total est d'environ 218 milliards de combinaisons. Ca se brute-force en 10 secondes.
SHA-256 est conçu pour etre rapide. C'est parfait pour vérifier l'intégrité d'un fichier ou signer un certificat TLS. Pour les mots de passe, la vitesse est l'ennemi.
MD5, c'est encore pire. Plus rapide, et des collisions connues en plus. Si tu vois MD5 dans du code d'authentification, c'est un signal d'alarme.
bcrypt : le choix par défaut
bcrypt date de 1999 (conçu par Niels Provos et David Mazieres pour OpenBSD) et reste le standard de facto pour le hashing de mots de passe. Trois propriétés le rendent adapte :
Il est volontairement lent. Le paramètre de "coût" (cost factor) contrôle combien de temps le hashing prend. Un coût de 12 signifie 2^12 = 4096 itérations internes. Sur mon serveur, ca prend environ 250ms. C'est imperceptible pour un utilisateur qui se connecte, mais catastrophique pour un attaquant qui essaie des millions de combinaisons.
Il inclut un sel (salt) automatiquement. Un sel est une valeur aleatoire ajoutee au mot de passe avant le hashing. Deux utilisateurs avec le meme mot de passe auront des hash différents. Ca rend les rainbow tables (des tables precalculees de hash) inutiles.
Il est disponible partout. Sur paltemps.fr, j'utilise bcrypt directement via Bun :
typescript// Hasher un mot de passe (a l'inscription ou au changement)
const hash = await Bun.password.hash("monsupermotdepasse", {
algorithm: "bcrypt",
cost: 12,
});
// Resultat : "$2b$12$LJ3m4ys3Lk0TSwMCkVc8UO8G4OPZRyPJ0JwDnlGOsCdaXRqzM1jK"
// Verifier un mot de passe (au login)
const isValid = await Bun.password.verify("monsupermotdepasse", hash);
// true ou false
Le hash contient tout : la version de bcrypt ($2b$), le coût ($12$), le sel et le hash lui-meme. Tu stockes cette chaîne dans ta base. Pas besoin de colonnes separees pour le sel.
argon2 : le successeur
Argon2 a gagne le Password Hashing Competition en 2015. C'est l'algorithme recommande par l'OWASP pour les nouvelles applications.
Son avantage sur bcrypt : il est "memory-hard". En plus du temps CPU, il consomme beaucoup de mémoire (configurable). Les GPU et les ASIC (puces specialisees) ont beaucoup de coeurs de calcul, mais pas tant de mémoire par coeur. Argon2 rend le brute-force sur GPU beaucoup plus coûteux que bcrypt.
Il existe trois variantes :
- argon2d : optimise contre les attaques GPU (data-dependent memory access)
- argon2i : optimise contre les attaques par canaux lateraux (side-channel)
- argon2id : hybride des deux. C'est celui que tu dois utiliser
typescript// Avec Bun
const hash = await Bun.password.hash("monsupermotdepasse", {
algorithm: "argon2id",
memoryCost: 65536, // 64 MB
timeCost: 2, // 2 iterations
});
const isValid = await Bun.password.verify("monsupermotdepasse", hash);
scrypt
Cree par Colin Percival en 2009, scrypt est aussi memory-hard. Il est utilise par certains systèmes (Linux, certaines cryptomonnaies). En pratique, pour le hashing de mots de passe en 2026, je recommande argon2id pour un nouveau projet et bcrypt si tu es sur un framework qui le supporte nativement.
Trouver le bon coût
Le coût ideal depend de ton serveur et de ce que tes utilisateurs tolerent. Le login devrait prendre entre 200ms et 500ms. Plus rapide, tu n'es pas assez protégé. Plus lent, l'experience utilisateur se degrade.
bash# Benchmark rapide pour bcrypt
bun -e "
const costs = [10, 11, 12, 13, 14];
for (const cost of costs) {
const start = performance.now();
await Bun.password.hash('test', { algorithm: 'bcrypt', cost });
console.log('cost ' + cost + ': ' + (performance.now() - start).toFixed(0) + 'ms');
}
"
Sur mon VPS (2 vCPU), un coût de 12 donne environ 250ms. Un coût de 13 donne 500ms. Un coût de 14 dépassé la seconde. Je reste a 12.
Les rainbow tables et pourquoi le sel les tue
Une rainbow table, c'est une base de donnees geante qui associe des mots de passe courants a leurs hash. "password123" -> "ef92...". Si tu hash sans sel, un attaquant qui vole ta base peut chercher chaque hash dans sa rainbow table et retrouver le mot de passe en quelques secondes.
Le sel rend ca impossible. Chaque mot de passe est hash avec un sel différent (16 octets aleatoires, typiquement). L'attaquant devrait precalculer une rainbow table pour chaque sel possible, ce qui est physiquement impossible.
bcrypt et argon2 incluent le sel automatiquement. Tu n'as pas a le gerer. Si tu utilises un algorithme qui ne le fait pas (SHA-256 par exemple), tu dois générer et stocker le sel toi-meme. Raison de plus pour utiliser bcrypt ou argon2.
Navigation : Precedent : 04 - Refresh tokens | Suivant : 06 - RBAC
Sources
- OWASP Password Storage Cheat Sheet par OWASP
- Password Hashing Competition par PHC
- Bun.password API par Bun
- How To Safely Store A Password par Coda Hale
Retrouve d'autres articles techniques sur paltemps.fr.