Comment stocker un mot de passe en base de données
Stocker un mot de passe en clair = faute professionnelle. Voici le pattern complet (Argon2id + salt + paramètres OWASP) avec exemples de code.
Stocker un mot de passe utilisateur dans une base de données est une opération critique. Mal faite, elle expose tous vos utilisateurs lors d'une fuite. Voici le pattern à jour en 2026, conforme OWASP, CNIL et ANSSI.
Ce qu'il ne faut JAMAIS faire
- Stocker en clair — interdit par le RGPD et toute norme de sécurité.
- Stocker chiffré avec une clé symétrique — fuite de la clé = fuite de tous les mots de passe.
- Hacher avec MD5 ou SHA-1 — algorithmes cassés et trop rapides.
- Hacher avec SHA-256 nu — trop rapide (20 milliards de tests/seconde sur GPU).
- Réutiliser le même salt — annule l'effet anti-rainbow-table.
- Stocker dans le code source — fuite Git = fuite des comptes.
Le pattern correct : Argon2id + salt unique
Recommandation OWASP 2026 :
- Algorithme : Argon2id (à défaut : scrypt, bcrypt avec cost ≥ 12, PBKDF2-HMAC-SHA-256 avec ≥ 600 000 itérations).
- Paramètres Argon2id :
m=65536(64 Mo de mémoire),t=3(3 itérations),p=4(4 threads). - Salt : 16 octets aléatoires uniques par utilisateur, généré avec un PRNG cryptographique.
- Sortie : chaîne self-describing au format PHC (
$argon2id$v=19$m=65536,t=3,p=4$<salt>$<hash>).
Exemple Node.js (argon2)
import argon2 from 'argon2';
// Création du compte
async function hashPassword(password) {
return argon2.hash(password, {
type: argon2.argon2id,
memoryCost: 65536, // 64 Mo
timeCost: 3,
parallelism: 4,
});
}
// Vérification
async function verifyPassword(stored, candidate) {
return argon2.verify(stored, candidate);
}
La librairie gère salt et paramètres automatiquement. La chaîne stockée contient tout ce qu'il faut pour vérifier (PHC format).
Exemple Python (argon2-cffi)
from argon2 import PasswordHasher
ph = PasswordHasher(
memory_cost=65536,
time_cost=3,
parallelism=4,
)
# Hachage
hashed = ph.hash("motdepasse_utilisateur")
# Vérification
try:
ph.verify(hashed, candidate)
# OK
except argon2.exceptions.VerifyMismatchError:
# Mot de passe incorrect
Exemple PHP (password_hash)
// PHP 7.4+ : Argon2id natif
$hash = password_hash($password, PASSWORD_ARGON2ID, [
'memory_cost' => 65536,
'time_cost' => 3,
'threads' => 4,
]);
// Vérification
if (password_verify($candidate, $hash)) {
// OK
}
// Migration : si l'algorithme ou les paramètres ont changé, on rehashe
if (password_needs_rehash($hash, PASSWORD_ARGON2ID, $options)) {
$newHash = password_hash($candidate, PASSWORD_ARGON2ID, $options);
// sauvegarder $newHash en base
}
Générer un salt Argon2id de test
Schéma SQL recommandé
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email VARCHAR(254) UNIQUE NOT NULL,
-- chaîne PHC complète : $argon2id$v=19$m=65536,t=3,p=4$<salt>$<hash>
pwd_hash TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
last_login TIMESTAMPTZ
);
Pas de colonne salt séparée : le format PHC contient déjà le salt, les paramètres et l'algorithme. Cela facilite la migration vers de meilleurs paramètres dans le temps.
Plan de migration depuis MD5 / SHA-1 / bcrypt
Vous héritez d'une base avec des hashes faibles ? Plan progressif :
- Ajoutez une colonne
pwd_hash_v2nullable etpwd_algo. - Au prochain login réussi de chaque utilisateur, recalculez le hash en Argon2id et stockez dans
pwd_hash_v2. - Marquez
pwd_algo = 'argon2id'; à la prochaine connexion, vérifiez d'abordpwd_hash_v2. - Après 6 à 12 mois, pour les utilisateurs qui ne se sont pas reconnectés, forcez une réinitialisation par e-mail.
- Supprimez l'ancienne colonne.
Bonnes pratiques complémentaires
- Vérifier les mots de passe choisis contre la liste Have I Been Pwned (API k-Anonymity).
- Limiter les tentatives de connexion (5-10 puis temporisation ou verrouillage).
- Logger les échecs de connexion (sans le mot de passe candidat).
- Activer la 2FA pour tous les comptes administrateurs.
- Forcer HTTPS sur tous les formulaires d'authentification.