Aller au contenu
Réussir mon BTS SIO

Tape une notion, une commande ou le nom d'une épreuve.

Par exemple

Chapitre 621 minIntermédiaire

Stocker un mot de passe : sel, bcrypt et Argon2

Par Josselyn C., prof de BTS SIO. Mis à jour le .

À la fin de ce chapitre, tu sauras

  • justifier le hachage plutôt que le chiffrement d'un mot de passe ;
  • expliquer le rôle du sel, du poivre et du facteur de coût ;
  • utiliser password_hash et password_verify, et migrer d'un vieux SHA-1 ;
  • calculer l'entropie d'une politique de mots de passe et la comparer à la CNIL.

5 exercices corrigés1 schéma

Stocker un mot de passe correctement, c'est ne jamais le stocker : on enregistre à la place une empreinte calculée par une fonction de hachage spécialisée, volontairement lente, avec un sel aléatoire propre à chaque compte, comme bcrypt ou Argon2id. À la connexion, on recalcule l'empreinte du mot de passe saisi et on la compare. Ce chapitre explique pourquoi ni le stockage en clair, ni le chiffrement, ni un SHA-256 ne conviennent, comment fonctionnent le sel et le facteur de coût, comment coder tout cela en PHP, Node.js et C#, et ce que recommandent la CNIL et l'ANSSI.

C'est la question de cryptographie la plus fréquente des sujets d'E7 SLAM : elle est tombée en 2022, en 2025 et en 2026.

Pourquoi ni en clair, ni chiffré

En clair, un mot de passe est exposé à quiconque lit la base : un administrateur, un prestataire, un attaquant qui a réussi une injection SQL, le voleur d'une sauvegarde. Et le dommage déborde ton application : la plupart des gens réutilisent leurs mots de passe, et l'attaquant essaiera le couple adresse et mot de passe sur leur messagerie, leur banque, leurs autres comptes professionnels.

Chiffré, le mot de passe est réversible : qui détient la clé peut tous les déchiffrer. Or la clé doit être accessible à l'application pour vérifier les connexions, donc sur le même serveur : un attaquant qui compromet le serveur récupère la base et la clé. Et l'on n'a jamais besoin de relire un mot de passe : il suffit de vérifier qu'un mot de passe saisi est le bon.

Une exception confirme la règle : le mot de passe qu'une application doit elle-même présenter à un autre système (le compte de service avec lequel elle se connecte à une base, à un annuaire, à une API). Celui-là doit pouvoir être relu, donc il se chiffre, avec une clé gardée hors de la base, ou mieux, il se range dans un coffre de secrets. Le sujet SLAM 2023 (cas Lama) en donne un exemple dans son dossier documentaire : le mot de passe d'un rôle applicatif y est « chiffré de façon réversible » parce que « le pilote de connexion à la base métier nécessite le mot de passe en clair ». Hacher sert à vérifier un secret qu'on reçoit ; chiffrer sert à garder un secret qu'on devra envoyer.

Corollaire : une application bien conçue ne peut pas renvoyer son mot de passe à un utilisateur qui l'a oublié. Si un site t'envoie ton ancien mot de passe par courriel, il le stocke en clair ou chiffré. La bonne procédure : un lien de réinitialisation à usage unique, contenant un jeton aléatoire, valable peu de temps, qui permet de choisir un nouveau mot de passe.

Pourquoi un hachage rapide ne suffit pas

SHA-256 est irréversible ; pourquoi ne pas l'utiliser ? Parce qu'il est conçu pour être rapide. L'attaquant qui vole la base ne cherche pas à inverser la fonction : il devine. Il hache des milliards de mots de passe candidats et compare les empreintes à celles de la base. C'est l'attaque hors ligne, sans aucune limite de tentatives, puisque ton application n'est plus dans la boucle.

Une carte graphique haut de gamme calcule de l'ordre de 20 milliards d'empreintes SHA-256 par seconde (bancs d'essai publics de l'outil hashcat). Prenons la politique du sujet E7 SLAM 2025 (cas Casterman) : des mots de passe de 6 caractères, choisis librement. Même s'ils mélangeaient majuscules, minuscules et chiffres (62 symboles), il y aurait 62⁶ ≈ 5,7 × 10¹⁰ combinaisons, toutes essayées en moins de 3 secondes. Et l'attaquant commence par les listes de mots de passe issus de fuites précédentes, des centaines de millions de vrais mots de passe, qui contiennent presque tous les choix humains.

D'où le cahier des charges d'une fonction de hachage de mots de passe :

  • lente, réglable par un facteur de coût : quelques dizaines de millisecondes pour toi à chaque connexion, mais des milliers de fois moins d'essais par seconde pour l'attaquant ;
  • gourmande en mémoire si possible, pour neutraliser les cartes graphiques et les circuits spécialisés, qui ont beaucoup de cœurs mais peu de mémoire par cœur ;
  • salée.

Avec bcrypt de coût 12, la même carte graphique ne calcule plus que de l'ordre de 1 400 empreintes par seconde : essayer les 62⁶ combinaisons prendrait plus d'un an, au lieu de 3 secondes.

Le sel : un aléa différent pour chaque compte

Sans sel, deux utilisateurs qui ont le même mot de passe ont la même empreinte. L'attaquant le voit immédiatement, et il peut utiliser des tables précalculées (dont les tables arc-en-ciel) : des milliards de couples « mot de passe, empreinte » calculés une fois pour toutes et réutilisés contre chaque base volée.

Le sel est une valeur aléatoire, différente pour chaque compte, ajoutée au mot de passe avant le hachage et stockée en clair à côté de l'empreinte. Il n'est pas secret ; il rend chaque empreinte unique.

Sans sel : SHA-256 seulInèsSoleil2026SHA-2562db95263a0a9…9d8b33MarcSoleil2026SHA-2562db95263a0a9…9d8b33Même empreinte : le doublon se voit, une table précalculée suffitAvec un sel aléatoire par compte : Argon2idInès : Soleil2026sel 7f3a91d2…Argon2idlent, salé…$7f3a91d2…$Qk9fZ2…Marc : Soleil2026sel c19e04b7…Argon2idlent, salé…$c19e04b7…$Lm03Tb…Empreintes différentes : chaque compte doit être attaqué séparément
Sans sel, un même mot de passe donne la même empreinte ; avec un sel par compte, chaque empreinte est unique

Le sel apporte trois choses :

  1. deux mots de passe identiques donnent des empreintes différentes : l'attaquant ne repère plus les doublons ;
  2. les tables précalculées deviennent inutiles : il faudrait en calculer une par valeur de sel ;
  3. l'attaquant doit attaquer chaque compte séparément : le coût de l'attaque est multiplié par le nombre de comptes.

La CNIL recommande un sel « généré aléatoirement » d'une « longueur minimale de 128 bits », « généré pour chaque utilisateur et stocké dans la même base de données que les mots de passe ». Les fonctions modernes (bcrypt, Argon2id) génèrent le sel elles-mêmes et l'enregistrent dans la chaîne produite : tu n'as rien à gérer.

Le poivre (pepper) est un complément facultatif : une valeur secrète, la même pour tous les comptes, ajoutée au calcul (souvent sous forme d'un HMAC) mais stockée hors de la base, dans la configuration du serveur ou un coffre de secrets. Un attaquant qui ne vole que la base, par une injection SQL ou une sauvegarde, ne peut alors rien tester sans le poivre.

bcrypt, Argon2id, scrypt, PBKDF2 : les fonctions de hachage de mots de passe

FonctionAnnéeParamètres de coûtRéglage minimal conseillé par l'OWASPRemarques
Argon2id2015 (normalisé RFC 9106 en 2021)Mémoire, nombre de passes, parallélisme19 Mio de mémoire, 2 passes, parallélisme 1Vainqueur du concours Password Hashing Competition ; le premier choix aujourd'hui
bcrypt1999Coût c (2ᶜ itérations)Coût 10 au moins ; PHP utilise 12 par défaut depuis la version 8.4Très répandu ; ne prend en compte que les 72 premiers octets du mot de passe
scrypt2009Mémoire et tempsN = 2¹⁷, r = 8, p = 1Gourmand en mémoire, utilisé par certains gestionnaires de mots de passe
PBKDF22000Nombre d'itérations600 000 itérations avec HMAC-SHA-256Choisi quand une norme impose un algorithme validé (FIPS) ; ne résiste pas aux cartes graphiques

Les deux variantes d'Argon2 qu'on croise dans les sujets : Argon2i, conçue pour résister aux attaques par canaux auxiliaires, et Argon2id, un mélange d'Argon2i et d'Argon2d, recommandé par la RFC 9106 et par l'OWASP pour les mots de passe. Si un sujet demande Argon2i, utilise-le ; si tu choisis, prends Argon2id.

Ces fonctions produisent une chaîne qui contient tout ce qu'il faut pour vérifier : l'algorithme, les paramètres, le sel et l'empreinte. Une colonne VARCHAR(255) suffit.

text
$2y$12$9fNnOE0tqW1JX1ZPz2Uyp.Wn7hY1u7s6m5h3k0y8bQq1K1o2cW3a2
 │   │  └ sel (22 caractères) puis empreinte (31 caractères)
 │   └ coût : 2¹² itérations
 └ algorithme : bcrypt

$argon2id$v=19$m=65536,t=4,p=1$eziCjheI5regP4il9EX1HQ$Rq1m…
 │          │    │                └ sel en Base64       └ empreinte
 │          │    └ 64 Mio de mémoire, 4 passes, 1 fil d'exécution
 │          └ version de l'algorithme
 └ algorithme : Argon2id

En PHP : password_hash, password_verify, password_needs_rehash

PHP fournit tout depuis la version 5.5. Trois fonctions suffisent, et elles gèrent le sel toutes seules :

php
<?php
// Inscription : calculer l'empreinte (colonne mot_de_passe VARCHAR(255))
$hache = password_hash($motDePasse, PASSWORD_ARGON2ID);   // ou PASSWORD_DEFAULT (bcrypt)
$stmt = $pdo->prepare('INSERT INTO client (email, mot_de_passe) VALUES (?, ?)');
$stmt->execute([$email, $hache]);

// Connexion : vérifier
$stmt = $pdo->prepare('SELECT id, mot_de_passe FROM client WHERE email = ?');
$stmt->execute([$email]);
$client = $stmt->fetch();
if ($client !== false && password_verify($motDePasse, $client['mot_de_passe'])) {
    session_regenerate_id(true);
    $_SESSION['client_id'] = $client['id'];
} else {
    // Message identique que l'adresse existe ou non : ne pas aider l'attaquant
    $erreur = 'Adresse ou mot de passe incorrect.';
}

password_verify() lit l'algorithme, le coût et le sel dans la chaîne stockée, recalcule l'empreinte du mot de passe saisi et compare les deux en temps constant. Ne compare jamais deux empreintes avec == et ne recalcule jamais toi-même l'empreinte pour la passer dans le WHERE d'une requête : avec un sel aléatoire, elle serait différente à chaque calcul.

Migrer depuis un vieux SHA-1. C'est la situation du sujet E7 SLAM 2026 (cas NDV) : la base contient des empreintes SHA1($motDePasse) et il faut passer à Argon2. On ne peut pas convertir les empreintes existantes, puisqu'on ne connaît pas les mots de passe. On les migre à la connexion, au moment où l'utilisateur saisit son mot de passe :

php
<?php
$hache = $client['mot_de_passe'];
$ancienFormat = !str_starts_with($hache, '$');           // 40 caractères hexadécimaux : SHA-1
$valide = $ancienFormat
    ? hash_equals($hache, sha1($motDePasse))             // comparaison en temps constant
    : password_verify($motDePasse, $hache);

if ($valide && ($ancienFormat || password_needs_rehash($hache, PASSWORD_ARGON2ID))) {
    $nouveau = password_hash($motDePasse, PASSWORD_ARGON2ID);
    $maj = $pdo->prepare('UPDATE client SET mot_de_passe = ? WHERE id = ?');
    $maj->execute([$nouveau, $client['id']]);
}

password_needs_rehash() sert aussi plus tard : quand tu augmentes le coût ou changes d'algorithme, chaque empreinte est recalculée à la connexion suivante. Pour les comptes qui ne se reconnectent jamais, on peut envelopper l'ancienne empreinte (password_hash(sha1_existant, PASSWORD_ARGON2ID)) en marquant le compte, ou forcer une réinitialisation par courriel au bout d'un délai.

En Node.js et en C#

En Node.js, le paquet npm argon2 (ou bcrypt) joue le rôle de password_hash :

js
import argon2 from 'argon2';

const hache = await argon2.hash(motDePasse);          // Argon2id par défaut, sel inclus
if (await argon2.verify(hache, motDePasse)) {
  // connexion acceptée
}

En C#, le paquet NuGet BCrypt.Net-Next est le plus utilisé, et ASP.NET Core Identity fournit son propre PasswordHasher, fondé sur PBKDF2 :

csharp
string hache = BCrypt.Net.BCrypt.HashPassword(motDePasse, workFactor: 12);
bool valide = BCrypt.Net.BCrypt.Verify(motDePasse, hache);

Quel que soit le langage, la règle est la même : une fonction de la bibliothèque standard ou d'une bibliothèque reconnue, jamais un assemblage maison du genre sha256(sel + motDePasse).

La politique de mots de passe : ce que recommandent la CNIL et l'ANSSI

Hacher correctement ne sert à rien si les utilisateurs choisissent 123456. La CNIL a fixé ses attentes dans une recommandation de 2022 (délibération n° 2022-100 du 21 juillet 2022), alignée sur le guide de l'ANSSI « Recommandations relatives à l'authentification multifacteur et aux mots de passe ». Elle raisonne en entropie, mesurée en bits : un mot de passe de L caractères tirés au hasard parmi N symboles a une entropie de L × log₂(N) bits.

Cas de la CNILEntropie minimaleExemples donnés par la CNIL
1. Mot de passe seul80 bits12 caractères avec majuscules, minuscules, chiffres et caractères spéciaux (parmi au moins 37) ; ou 14 caractères avec majuscules, minuscules et chiffres ; ou une phrase de passe de 7 mots
2. Mot de passe et restriction d'accès au compte50 bits8 caractères comprenant 3 des 4 catégories ; ou une phrase de 5 mots ; ou 16 chiffres. La restriction : temporisation croissante, nombre d'essais limité, captcha, ou blocage après au plus 10 échecs consécutifs
3. Code de déverrouillage d'un matériel détenu par la personne (carte à puce, clé contenant un certificat)13 bits4 chiffres, avec blocage après au plus 3 échecs

Vérifions le premier exemple : 26 majuscules + 26 minuscules + 10 chiffres + 37 caractères spéciaux = 99 symboles ; 12 × log₂(99) ≈ 12 × 6,63 ≈ 79,6 bits, soit environ 80. Le deuxième : 14 × log₂(62) ≈ 14 × 5,95 ≈ 83,4 bits.

D'autres points de la recommandation tombent souvent :

  • les responsables de traitement « ne devraient plus imposer de modification périodique des mots de passe » à tous les utilisateurs ; un renouvellement périodique reste nécessaire pour les comptes à privilèges ;
  • un mot de passe doit être changé dès qu'il est compromis, et l'utilisateur prévenu en cas de fuite ;
  • les mots de passe couramment utilisés doivent être refusés, en informant l'utilisateur ;
  • le mot de passe ne doit jamais être transmis en clair, ni affiché, ni envoyé par courriel.

La meilleure protection reste de ne pas dépendre du seul mot de passe : l'authentification multifacteur (code à usage unique, clé de sécurité FIDO2, ou passkey, une paire de clés stockée sur l'appareil qui remplace le mot de passe). Le cours sur l'authentification et les habilitations développera ces mécanismes.

À l'E7 : comment la notion est demandée

SessionQuestionCe qui est attendu
SLAM 2022 (cas Denas)Choisir entre des mots de passe chiffrés par AES et des mots de passe hachés par SHA ; expliquer l'importance d'ajouter un grain de sel avec bcryptLe hachage, irréversible ; le sel contre les doublons et les tables précalculées
SLAM 2025 (cas Casterman)Expliquer pourquoi supprimer les mots de passe en clair ; choisir entre empreinte et mot de passe chiffré, en justifiantFuite, réutilisation des mots de passe, accès des administrateurs ; l'empreinte, irréversible
SLAM 2026 (cas NDV)Identifier l'algorithme de hachage utilisé (SHA1()) et dire pourquoi il n'est pas efficace, nommer la faille OWASP ; le remplacer par Argon2i dans le code d'inscription et de connexionSHA-1 rapide et cassé, défaillance cryptographique (A02 du Top 10 OWASP 2021) ; password_hash($mdp, PASSWORD_ARGON2I) et password_verify()

Le document fourni avec le sujet 2026 présente SHA-256 comme « robuste et largement utilisé pour le stockage sécurisé des mots de passe ». Ce n'est vrai qu'avec un sel et un très grand nombre d'itérations (c'est-à-dire PBKDF2) : un SHA-256 simple est bien trop rapide. Le même document, lui, recommande sans ambiguïté bcrypt et Argon2i, conçus pour cet usage.

En SISR, la même logique s'applique aux comptes des équipements et des annuaires : les mots de passe des comptes locaux d'un commutateur se stockent avec un algorithme fort (sur les équipements Cisco, le type 8 ou 9 plutôt que le type 7, simplement réversible), et la politique de mots de passe de l'annuaire Active Directory se règle par stratégie de groupe.

Pour aller plus loin. Longueur, complexité, verrouillage et réinitialisation sont traités dans le chapitre Politique de mots de passe du cours Authentification.

Exercices

Exercice 1 sur 5 Lire des empreintes Réussi À revoir

Tu audites la table utilisateur d'une application. Pour chaque valeur de la colonne mot_de_passe, dis comment le mot de passe est stocké et si c'est acceptable.

  1. Soleil2026
  2. e10adc3949ba59abbe56e057f20f883e
  3. 7c4a8d09ca3762af61e59520943dc26494f8941b
  4. $2y$10$Gq3o7mX1vYkU7pZ4bT2eLu… (60 caractères)
  5. $argon2id$v=19$m=65536,t=4,p=1$…
Voir la réponseMasquer la réponse de l'exercice 1
  1. En clair : inacceptable.
  2. 32 caractères hexadécimaux : MD5 sans sel (c'est d'ailleurs l'empreinte MD5 de 123456, présente dans toutes les tables). Inacceptable.
  3. 40 caractères : SHA-1 sans sel (l'empreinte de 123456). Inacceptable.
  4. bcrypt de coût 10, sel inclus : acceptable ; on peut monter le coût à 12 et laisser password_needs_rehash() mettre à jour les empreintes aux connexions suivantes.
  5. Argon2id (64 Mio, 4 passes), sel inclus : la meilleure option.

Tu avais trouvé ?

Exercice 2 sur 5 Combien de temps pour tout essayer ? Réussi À revoir

Une politique impose des mots de passe de 8 caractères parmi les 62 lettres et chiffres. Un attaquant dispose d'une carte graphique qui calcule 2,2 × 10¹⁰ SHA-256 par seconde, ou 1 400 empreintes bcrypt de coût 12 par seconde.

  1. Combien y a-t-il de mots de passe possibles ?
  2. Combien de temps faut-il pour tous les essayer si la base utilise SHA-256 ? bcrypt de coût 12 ?
  3. Pourquoi ce calcul est-il optimiste pour le défenseur ?
Voir la réponseMasquer la réponse de l'exercice 2
  1. 62⁸ ≈ 2,18 × 10¹⁴ mots de passe.
  2. SHA-256 : 2,18 × 10¹⁴ / 2,2 × 10¹⁰ ≈ 9 900 secondes, soit environ 2 h 45. bcrypt de coût 12 : 2,18 × 10¹⁴ / 1 400 ≈ 1,56 × 10¹¹ secondes, soit environ 4 900 ans.
  3. Les gens ne choisissent pas au hasard : l'attaquant essaie d'abord les mots de passe déjà vus dans des fuites et les variantes courantes (Prenom2026!), et en trouve une grande partie en quelques minutes, même avec bcrypt. D'où la liste de mots de passe refusés, la longueur minimale et l'authentification multifacteur.

Tu avais trouvé ?

Exercice 3 sur 5 Conforme à la CNIL ? Réussi À revoir

Calcule l'entropie de chaque politique et dis dans quel cas de la CNIL elle est acceptable.

  1. 10 caractères parmi les 62 lettres et chiffres, sans limitation des tentatives.
  2. La même politique, avec blocage du compte après 5 échecs consécutifs.
  3. 6 caractères libres, comme dans le sujet 2025 (en supposant 62 symboles).
Voir la réponseMasquer la réponse de l'exercice 3
  1. 10 × log₂(62) ≈ 10 × 5,95 ≈ 59,5 bits : en dessous des 80 bits exigés pour un mot de passe seul. Non conforme.
  2. Toujours 59,5 bits, mais avec une restriction d'accès (blocage après 5 échecs, donc au plus 10) : le cas 2 demande 50 bits. Conforme.
  3. 6 × 5,95 ≈ 35,7 bits : non conforme, même avec une restriction d'accès.

Tu avais trouvé ?

Exercice 4 sur 5 Corriger une inscription Réussi À revoir

Voici le code d'inscription d'un site :

php
$hache = sha1($_POST['password']);
$req = $pdo->prepare('INSERT INTO compte (login, password) VALUES (:login, :password)');
$req->bindParam(':login', $_POST['login']);
$req->bindParam(':password', $hache);
$req->execute();
  1. Quel est le problème, et quelle catégorie du Top 10 OWASP est concernée ?
  2. Corrige le code d'inscription pour utiliser Argon2i, comme le demande le sujet.
  3. Que faut-il changer dans le code de connexion ?
Voir la réponseMasquer la réponse de l'exercice 4
  1. Le mot de passe est haché avec SHA-1, sans sel et beaucoup trop rapide : une base volée se casse en quelques minutes. C'est une défaillance cryptographique, A02 dans le Top 10 OWASP 2021 (A04 dans l'édition 2025).
  2. Seule la première ligne change : $hache = password_hash($_POST['password'], PASSWORD_ARGON2I); Vérifie aussi que la colonne password fait au moins 255 caractères (une empreinte Argon2 en fait environ une centaine, contre 40 pour SHA-1).
  3. On ne compare plus sha1($saisie) à la colonne dans le WHERE : on lit l'empreinte du compte par son login (SELECT password FROM compte WHERE login = ?), puis on teste password_verify($_POST['password'], $empreinte).

Tu avais trouvé ?

Exercice 5 sur 5 Le sel et le poivre Réussi À revoir

  1. Un développeur utilise le même sel, Helianthe!, pour tous les comptes. Qu'est-ce que cela change par rapport à l'absence de sel ?
  2. Où stocke-t-on le sel ? Et le poivre ?
  3. Un attaquant vole la base de données par une injection SQL, mais pas les fichiers du serveur. Lequel des deux, sel ou poivre, le gêne ?
Voir la réponseMasquer la réponse de l'exercice 5
  1. Presque rien : il empêche l'usage des tables précalculées génériques, mais deux mots de passe identiques ont toujours la même empreinte, et l'attaquant ne calcule qu'une seule table, valable pour tous les comptes. Le sel doit être aléatoire et différent pour chaque compte.
  2. Le sel se stocke avec l'empreinte, dans la base, en clair : il n'est pas secret (bcrypt et Argon2 l'intègrent à la chaîne). Le poivre se stocke hors de la base : configuration du serveur, variable d'environnement, coffre de secrets.
  3. Le poivre : sans lui, l'attaquant ne peut tester aucun mot de passe. Le sel, lui, est dans la base volée ; il l'oblige seulement à attaquer chaque compte séparément.

Tu avais trouvé ?

Chapitre terminé !

Fais les 5 exercices avant de passer à la suite : c'est en t'entraînant que tout se fixe.

chapitres
–
de série
–
du cours
–
Chapitre suivant : HMAC et signature numérique : intégrité, authenticité, preuveRevoir le sommaire du cours