ToolPal
Un cadenas à combinaison sur un clavier symbolisant la sécurité des mots de passe

Hachage de mots de passe avec bcrypt : le guide pratique du développeur

📷 Pixabay / Pexels

Hachage de mots de passe avec bcrypt : le guide pratique du développeur

Tout ce que vous devez savoir sur bcrypt pour la sécurité des mots de passe — facteurs de coût, implémentation en Node.js/Python/PHP, et erreurs courantes à éviter.

DPar Daniel Park7 avril 20268 min de lecture

Si vous avez déjà dû stocker des mots de passe d'utilisateurs, on vous a probablement dit d'utiliser bcrypt. C'est la recommandation standard depuis plus de deux décennies, et pour de bonnes raisons. Mais comprendre pourquoi ça fonctionne — et comment l'utiliser correctement — c'est ce qui distingue une implémentation sécurisée d'une qui paraît seulement sécurisée.

Ce guide couvre tout ce qui est pratique : comment bcrypt fonctionne réellement, quel facteur de coût choisir, comment l'implémenter dans plusieurs langages, et les erreurs qui compromettent silencieusement la sécurité des mots de passe.

Ce qu'est bcrypt (et pourquoi il existe)

bcrypt a été conçu par Niels Provos et David Mazières en 1999, spécifiquement pour le hachage de mots de passe. Ce contexte est important. Il n'a pas été construit pour être rapide — il a été construit pour être lent de manière contrôlée et réglable.

L'idée fondamentale derrière bcrypt est que le hachage rapide est une vulnérabilité en matière de mots de passe. Un hachage généraliste comme SHA-256 peut calculer des milliards de hachages par seconde sur du matériel moderne. C'est excellent pour les sommes de contrôle et l'intégrité des données. C'est terrible pour les mots de passe, car un attaquant qui vole votre base de données de mots de passe peut tenter des milliards de suppositions par seconde avec des GPU courants.

bcrypt résout ce problème en introduisant un facteur de coût — un paramètre qui contrôle combien de travail l'algorithme effectue. Vous décidez de la lenteur du hachage. Le matériel devient plus rapide chaque année, donc vous pouvez augmenter le facteur de coût au fil du temps pour rester en avance.

Comment bcrypt fonctionne réellement

Lorsque vous appelez bcrypt.hash("mypassword", 10), plusieurs choses se produisent en interne :

  1. Un sel aléatoire de 16 octets est généré. Ce sel est unique à chaque opération de hachage.
  2. Le mot de passe et le sel sont introduits dans la configuration de clé Eksblowfish. C'est l'algorithme central de bcrypt — une version modifiée du chiffrement Blowfish avec une phase d'expansion de clé coûteuse.
  3. La configuration de clé est répétée 2^coût fois. Avec le facteur de coût 10, c'est 1 024 itérations. Avec le coût 12, c'est 4 096 itérations. Chaque incrément double le travail.
  4. Le hachage résultant est encodé sous forme de chaîne de 60 caractères contenant la version de l'algorithme, le facteur de coût, le sel et le hachage dans un seul élément.

La sortie ressemble à ceci :

$2b$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy

Décomposition :

  • $2b$ — version bcrypt
  • 10 — facteur de coût
  • Les 22 caractères suivants — le sel encodé en base64
  • Les caractères restants — le hachage

Comme le sel est intégré dans la chaîne de hachage, vous n'avez pas besoin de le stocker séparément. La fonction bcrypt.compare() extrait le sel du hachage stocké et l'utilise pour hacher le mot de passe d'entrée — aucune gestion manuelle du sel requise.

Pourquoi les fonctions de hachage rapides sont le mauvais outil

Les développeurs stockent parfois des mots de passe avec MD5, SHA-1 ou SHA-256. Ces algorithmes sont largement disponibles et bien compris, donc le choix semble raisonnable. Ce n'est pas le cas.

Le problème n'est pas que ces algorithmes sont cassés pour leurs usages prévus. Le problème est qu'ils sont conçus pour être rapides, et la rapidité est exactement ce qui ne faut pas pour le stockage des mots de passe.

En 2012, le chercheur en sécurité Jeremi Gosney a démontré le craquage de 90% d'une base de données de 6,4 millions de hachages SHA-1 en moins d'une heure. Avec du matériel moderne et des outils comme Hashcat, un attaquant avec un bon GPU peut tester des hachages MD5 à plus de 10 milliards de tentatives par seconde. SHA-256 ne vaut pas beaucoup mieux — environ 4 milliards par seconde.

bcrypt à coût 10 ramène cela à environ 20 000 tentatives par seconde. C'est tout l'intérêt.

Choisir le bon facteur de coût

Coût 10 est la valeur par défaut largement recommandée. Sur un serveur moderne, il produit un hachage en environ 100 à 300 ms. C'est imperceptiblement lent pour un utilisateur qui se connecte, mais cela signifie qu'un attaquant ne peut tester que quelques milliers de mots de passe par seconde par cœur.

Coût 12 quadruple à peu près le temps de calcul par rapport à 10. C'est un choix raisonnable pour les applications aux exigences de sécurité élevées — banque, santé, tout ce qui gère des données financières sensibles — où vous pouvez accepter des temps de connexion de 400 à 1000 ms.

Coût 14 et au-delà est généralement excessif pour les applications web. À coût 14, le hachage prend plusieurs secondes.

Ne descendez jamais en dessous de 8 en production. Certains anciens tutoriels montrent coût 5 ou 6 comme exemples. Ces valeurs sont trop rapides et offrent une protection insuffisante.

Implémenter bcrypt en Node.js

const bcrypt = require('bcrypt');

const COST_FACTOR = 10;

// Hachage d'un mot de passe
async function hashPassword(plaintext) {
  const hash = await bcrypt.hash(plaintext, COST_FACTOR);
  return hash; // Stocker cette chaîne dans votre base de données
}

// Vérification lors de la connexion
async function verifyPassword(plaintext, storedHash) {
  const match = await bcrypt.compare(plaintext, storedHash);
  return match; // true ou false
}

// Exemple d'utilisation
const hash = await hashPassword('hunter2');
console.log(hash); // $2b$10$...

const valid = await verifyPassword('hunter2', hash);
console.log(valid); // true

Implémenter bcrypt en Python

import bcrypt

COST_FACTOR = 10

def hash_password(plaintext: str) -> bytes:
    password_bytes = plaintext.encode('utf-8')
    hashed = bcrypt.hashpw(password_bytes, bcrypt.gensalt(rounds=COST_FACTOR))
    return hashed

def verify_password(plaintext: str, stored_hash: bytes) -> bool:
    password_bytes = plaintext.encode('utf-8')
    return bcrypt.checkpw(password_bytes, stored_hash)

# Utilisation
hashed = hash_password("hunter2")
print(hashed)  # b'$2b$10$...'

print(verify_password("hunter2", hashed))  # True
print(verify_password("wrong", hashed))    # False

Implémenter bcrypt en PHP

<?php
$options = ['cost' => 10];

// Hachage
$hash = password_hash($plaintext, PASSWORD_BCRYPT, $options);

// Vérification
$valid = password_verify($plaintext, $hash);

// Vérifier si le hachage doit être mis à niveau
if (password_needs_rehash($hash, PASSWORD_BCRYPT, $options)) {
    $newHash = password_hash($plaintext, PASSWORD_BCRYPT, $options);
    // Sauvegarder $newHash dans la base de données
}
?>

Implémenter bcrypt en Go

import "golang.org/x/crypto/bcrypt"

func HashPassword(password string) (string, error) {
    bytes, err := bcrypt.GenerateFromPassword([]byte(password), 10)
    return string(bytes), err
}

func CheckPasswordHash(password, hash string) bool {
    err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
    return err == nil
}

Erreurs courantes qui compromettent bcrypt

Re-hacher le mot de passe à chaque vérification de connexion. C'est une erreur de performance significative. Lorsqu'un utilisateur se connecte, vous devez comparer son mot de passe au hachage stocké en utilisant bcrypt.compare(). Ne hachez pas le mot de passe à nouveau et ne comparez pas les deux hachages — cela ne correspondra jamais, car un nouveau sel aléatoire est généré à chaque fois.

Stocker le mot de passe en clair. Évident, mais cela arrive.

Utiliser MD5 ou SHA pour le stockage des mots de passe. Même avec un sel ajouté manuellement, les hachages rapides donnent aux attaquants trop de débit.

Ne pas gérer la limite de 72 octets. bcrypt tronque silencieusement les entrées à 72 octets.

Utiliser un facteur de coût trop bas. Si vous utilisez coût 4 parce que c'est plus rapide, vous manquez l'essentiel. La lenteur est une propriété de sécurité, pas un bug.

Quand utiliser Argon2 à la place

bcrypt est la valeur par défaut sûre depuis 25 ans, mais ce n'est pas le meilleur choix moderne. Argon2 — spécifiquement Argon2id — a remporté le Password Hashing Competition en 2015 et répond à la principale faiblesse de bcrypt.

bcrypt est lié au CPU : un attaquant avec des GPU peut paralléliser le craquage relativement bon marché car l'algorithme ne nécessite pas beaucoup de mémoire. Argon2 est intensif en mémoire : il nécessite une quantité configurable de RAM par opération de hachage. Les GPU ont bien moins de mémoire par cœur de calcul qu'un CPU, donc les algorithmes intensifs en mémoire nivellent considérablement le terrain.

Résumé

  • La force de bcrypt vient d'une lenteur délibérée, pas de la complexité cryptographique
  • Utilisez toujours bcrypt.compare() (ou l'équivalent de votre langage) pour la vérification — ne jamais hacher et comparer
  • Le facteur de coût 10 est la bonne valeur par défaut pour la plupart des applications ; réévaluez tous les quelques années
  • bcrypt gère le salage automatiquement — vous n'avez pas besoin de gérer les sels
  • Soyez conscient de la limite de troncature à 72 octets pour les mots de passe inhabituellement longs
  • Pour les nouveaux projets, Argon2id mérite d'être envisagé ; pour les systèmes bcrypt existants, restez sur votre lancée

Le stockage des mots de passe est l'un des rares domaines où l'option sécurisée — bcrypt avec un facteur de coût raisonnable — est également simple à implémenter correctement. La plupart des vulnérabilités dans cet espace proviennent du fait d'ignorer les conseils, pas des failles dans l'algorithme lui-même.

Questions Fréquentes

D

À propos de l'auteur

Daniel Park

Senior frontend engineer based in Seoul. Seven years of experience building web applications at Korean SaaS companies, with a focus on developer tooling, web performance, and privacy-first architecture. Open-source contributor to the JavaScript ecosystem and founder of ToolPal.

En savoir plus

Partager

XLinkedIn

Articles associés