Aller au contenu
Réussir mon BTS SIO

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

Par exemple

Chapitre 617 minIntermédiaire

Défaillances cryptographiques (A04) : hacher, chiffrer, HTTPS

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

À la fin de ce chapitre, tu sauras

  • distinguer hachage, chiffrement et encodage ;
  • stocker un mot de passe avec un algorithme lent et salé ;
  • chiffrer une donnée qu'on doit relire sans exposer la clé ;
  • expliquer ce que TLS protège ;
  • générer un jeton imprévisible.

4 exercices corrigés1 schéma

Les défaillances cryptographiques (Cryptographic Failures, A04:2025) regroupent les données sensibles mal protégées : transmises sans chiffrement, stockées en clair ou avec un algorithme trop faible, protégées par des clés mal gardées ou un aléatoire prévisible. Ce chapitre explique la différence entre hachage et chiffrement, comment stocker un mot de passe, comment chiffrer une donnée qu'on doit relire, à quoi sert TLS et quelles erreurs éviter.

Identifier les données à protéger

Avant de choisir un algorithme, on classe les données. Dans AppliFrais :

DonnéeSensibilitéCe qu'il faut
Mot de passeCritique : il ouvre le compte, et souvent d'autres comptes de la personneNe jamais le stocker, même chiffré : on stocke une empreinte lente et salée
RIB du visiteurÉlevée : donnée financièreChiffrée en base, déchiffrée seulement pour la mise en paiement
Montants, déplacementsMoyenne : données personnellesAccès contrôlé, chiffrement du disque et des sauvegardes
Barème kilométriquePubliqueAucune confidentialité ; seulement l'intégrité

Les données sont à protéger à deux moments : en transit, quand elles circulent sur le réseau (TLS), et au repos, quand elles sont stockées en base, sur disque ou dans une sauvegarde (hachage, chiffrement).

Hachage ou chiffrement : la différence

HachageChiffrement
SensÀ sens unique : impossible de retrouver la donnée d'origineRéversible : avec la clé, on retrouve la donnée
CléAucuneUne clé (symétrique) ou une paire de clés (asymétrique)
Taille du résultatFixe, quelle que soit l'entréeProche de la taille de l'entrée
UsageVérifier un mot de passe, contrôler l'intégrité d'un fichierProtéger une donnée qu'on doit relire : RIB, dossier, message
ExemplesArgon2id, bcrypt, scrypt (mots de passe) ; SHA-256, SHA-3 (intégrité)AES-256-GCM, ChaCha20 (symétrique) ; RSA, courbes elliptiques (asymétrique)

Une fonction de hachage transforme une entrée de taille quelconque en une empreinte de taille fixe. Elle est déterministe (la même entrée donne toujours la même empreinte), à sens unique, et la moindre modification de l'entrée change toute l'empreinte :

SHA-256("azerty") = f2d81a260dea8a100dd517984e53c56a7523d96942a834b9cdc249bd4e8c7aa9
SHA-256("azertz") = 7dd6e0b180a8a39b2480194fd498092b474415346da395c3cd12b9ca1b8a5a8c

Stocker un mot de passe

On ne stocke jamais un mot de passe, ni en clair, ni chiffré : avec la clé, un attaquant ou un administrateur indélicat déchiffrerait tout. On stocke son empreinte. À la connexion, on calcule l'empreinte du mot de passe saisi et on la compare à celle de la base.

Mais une simple empreinte SHA-256 ne suffit pas, pour deux raisons.

Les attaques par dictionnaire et par tables précalculées. Les mêmes mots de passe donnent les mêmes empreintes. Un attaquant qui vole la base compare les empreintes à des listes d'empreintes de mots de passe courants (rainbow tables) : f2d81a26… est reconnu immédiatement comme azerty. La parade est le sel : une valeur aléatoire, différente pour chaque compte, ajoutée au mot de passe avant le hachage et stockée à côté de l'empreinte. Deux utilisateurs qui ont choisi azerty ont alors des empreintes différentes, et les tables précalculées ne servent plus à rien.

La vitesse. SHA-256 et MD5 sont conçus pour être rapides. Une seule carte graphique calcule des milliards d'empreintes MD5 par seconde : essayer toutes les combinaisons d'un mot de passe court devient trivial. Pour les mots de passe, on veut au contraire une fonction volontairement lente et gourmande en mémoire, dont on règle le coût.

Avec un sel aléatoire et Argon2idAliceazerty+ sel9f2c41…Argon2idlentempreinte stockée…9f2c41…Qm4Tz…Brunoazerty+ seld71e08…Argon2idlentempreinte stockée…d71e08…xP0aL…deux empreintes différentesSans sel, avec SHA-256AliceazertyBrunoazertySHA-256rapidef2d81a26…identique ×2tableprécalculée= azerty
Le sel rend chaque empreinte unique ; la lenteur de l'algorithme freine la force brute

Les ordres de grandeur suivants montrent l'effet de la lenteur. Ils supposent un attaquant qui teste toutes les combinaisons d'un mot de passe de 8 caractères parmi les 62 lettres et chiffres, soit 62⁸ ≈ 2,18 × 10¹⁴ possibilités :

AlgorithmeVitesse d'essai (ordre de grandeur, une carte graphique)Temps pour tout essayer
MD510¹⁰ empreintes par secondeenviron 6 heures
bcrypt, coût 1210⁴ empreintes par secondeenviron 690 ans

Les vitesses réelles dépendent du matériel ; l'écart, lui, reste de plusieurs ordres de grandeur.

Les algorithmes recommandés par l'OWASP pour les mots de passe :

AlgorithmeRecommandation
Argon2idPremier choix : au minimum 19 Mio de mémoire, 2 itérations, 1 degré de parallélisme
scryptSi Argon2id n'est pas disponible
bcryptPour les systèmes existants : facteur de coût d'au moins 10 ; limité à 72 octets de mot de passe
PBKDF2-HMAC-SHA256Quand une conformité FIPS l'impose : 600 000 itérations
MD5, SHA-1, SHA-256 seulsJamais pour un mot de passe

Ces fonctions génèrent le sel elles-mêmes et l'intègrent à l'empreinte, avec l'algorithme et le coût. Tu n'as rien à gérer à la main. En PHP :

php
// Inscription : PASSWORD_DEFAULT utilise bcrypt, avec un coût de 12 depuis PHP 8.4
$hash = password_hash($motDePasse, PASSWORD_DEFAULT);
// ou, si PHP a été compilé avec Argon2 :
$hash = password_hash($motDePasse, PASSWORD_ARGON2ID);
// $hash ressemble à : $2y$12$… (algorithme, coût, sel et empreinte dans une seule chaîne)

// Connexion
if (password_verify($saisie, $hashEnBase)) {
    if (password_needs_rehash($hashEnBase, PASSWORD_DEFAULT)) {
        // l'algorithme ou le coût par défaut a changé : on met l'empreinte à jour
        $nouveau = password_hash($saisie, PASSWORD_DEFAULT);
        // UPDATE utilisateur SET mdp_hash = ? WHERE id = ?
    }
    // connexion réussie
}

En Node.js, avec le paquet argon2 :

js
import argon2 from 'argon2';

const hash = await argon2.hash(motDePasse);            // Argon2id par défaut, sel aléatoire inclus
const valide = await argon2.verify(hashEnBase, saisie); // true ou false

La colonne mdp_hash doit être assez longue : 255 caractères (VARCHAR(255)) laissent de la marge pour un changement d'algorithme.

Chiffrer une donnée qu'on doit relire

Le RIB doit être relu pour payer : on le chiffre. Le chiffrement symétrique utilise la même clé pour chiffrer et déchiffrer ; c'est le bon choix pour des données stockées. L'algorithme de référence est AES, avec une clé de 256 bits et le mode GCM, qui chiffre et garantit en plus l'intégrité : toute modification du texte chiffré est détectée au déchiffrement.

js
import crypto from 'node:crypto';

// Clé de 32 octets (256 bits), lue depuis un secret, jamais écrite dans le code
const cle = Buffer.from(process.env.CLE_RIB, 'hex');

export function chiffrer(texte) {
  const iv = crypto.randomBytes(12);                       // vecteur d'initialisation, unique à chaque fois
  const chiffreur = crypto.createCipheriv('aes-256-gcm', cle, iv);
  const chiffre = Buffer.concat([chiffreur.update(texte, 'utf8'), chiffreur.final()]);
  const tag = chiffreur.getAuthTag();                      // 16 octets qui garantissent l'intégrité
  return Buffer.concat([iv, tag, chiffre]).toString('base64');
}

export function dechiffrer(donnee) {
  const brut = Buffer.from(donnee, 'base64');
  const iv = brut.subarray(0, 12), tag = brut.subarray(12, 28), chiffre = brut.subarray(28);
  const dechiffreur = crypto.createDecipheriv('aes-256-gcm', cle, iv);
  dechiffreur.setAuthTag(tag);                             // lève une erreur si la donnée a été modifiée
  return Buffer.concat([dechiffreur.update(chiffre), dechiffreur.final()]).toString('utf8');
}

Trois règles :

  1. Le vecteur d'initialisation (IV) est unique pour chaque chiffrement avec la même clé. Il n'est pas secret : on le stocke avec la donnée chiffrée. Réutiliser un IV avec AES-GCM détruit la sécurité.
  2. La clé ne se stocke pas à côté des données. Une clé dans la même base que les RIB chiffrés ne protège de rien si la base est volée. Elle va dans un coffre-fort de secrets ou un service de gestion de clés (KMS), et l'accès y est limité.
  3. On n'invente pas sa cryptographie. On utilise les fonctions éprouvées du langage (crypto de Node.js, sodium_crypto_secretbox ou openssl_encrypt en PHP) et des modes reconnus.

Les SGBD proposent aussi du chiffrement : chiffrement de colonnes, chiffrement transparent des fichiers de la base (TDE), chiffrement des sauvegardes. Le chapitre Injection SQL et protection des données du cours SQL montre AES_ENCRYPT sous MySQL et le chiffrement sous SQL Server, sujet d'annales de l'E7.

Chiffrement asymétrique et signature

Le chiffrement asymétrique utilise une paire de clés : une clé publique, que tout le monde peut connaître, et une clé privée, que seul son propriétaire détient. Ce qui est chiffré avec la clé publique ne se déchiffre qu'avec la clé privée. Il est lent, et sert surtout à deux choses :

  • établir une clé symétrique commune au début d'une connexion TLS (échange Diffie-Hellman, la clé privée du certificat servant à le signer pour prouver l'identité du serveur) ;
  • signer : le propriétaire calcule une signature avec sa clé privée ; tout le monde la vérifie avec la clé publique. La signature prouve l'origine et l'intégrité d'un document, d'une mise à jour ou d'un jeton JWT signé en RS256 ou ES256.

Les algorithmes courants : RSA (clés de 3 072 bits ou plus pour un usage durable), et les courbes elliptiques (ECDSA, Ed25519), plus rapides à sécurité égale. Les certificats, les autorités de certification et la PKI sont traités dans le cours Cybersécurité des infrastructures.

HTTPS et TLS : protéger les échanges

HTTPS est HTTP transporté dans une connexion chiffrée par TLS. TLS garantit trois choses : la confidentialité (personne sur le réseau ne lit le mot de passe ou le jeton), l'intégrité (personne ne modifie la page en route) et l'authentification du serveur (le certificat prouve que tu parles bien à frais.gsb.fr, et pas à un imposteur sur le Wi-Fi public).

Les règles actuelles :

  • HTTPS partout, pas seulement sur la page de connexion : un cookie de session envoyé une seule fois en HTTP peut être volé.
  • TLS 1.2 au minimum, TLS 1.3 de préférence. TLS 1.0 et 1.1 sont officiellement obsolètes depuis 2021 (RFC 8996), SSL depuis bien plus longtemps.
  • Un certificat valide, délivré par une autorité reconnue. Let's Encrypt en délivre gratuitement, renouvelés automatiquement. La durée de validité maximale des certificats diminue progressivement : 200 jours depuis mars 2026, et 47 jours prévus en 2029. Le renouvellement doit donc être automatisé.
  • Redirection de HTTP vers HTTPS et en-tête HSTS, qui interdit au navigateur de revenir en HTTP (chapitre 9).
  • Les connexions internes aussi : entre l'API et la base, entre l'application et un service de mail, quand elles traversent un réseau partagé.

La mise en place d'un certificat sur un hébergement est expliquée dans le cours HTML et CSS.

Générer de l'aléatoire sûr

Les jetons de session, les jetons de réinitialisation de mot de passe, les sels, les IV et les clés doivent être imprévisibles. Les générateurs ordinaires ne le sont pas : Math.random() en JavaScript, rand() et mt_rand() en PHP, java.util.Random produisent des suites qu'un attaquant peut prédire à partir de quelques valeurs.

LangageÀ éviter pour la sécuritéÀ utiliser
JavaScript (Node.js)Math.random()crypto.randomBytes(32), crypto.randomUUID(), crypto.randomInt()
PHPrand(), mt_rand(), uniqid()random_bytes(32), random_int()
Javajava.util.Randomjava.security.SecureRandom
C#System.RandomRandomNumberGenerator
php
$jeton = bin2hex(random_bytes(32));   // 64 caractères hexadécimaux, 256 bits d'aléa

Les erreurs cryptographiques classiques

ErreurPourquoi c'est graveLa bonne pratique
Mots de passe en clair ou chiffrésUne fuite les révèle tousArgon2id ou bcrypt
MD5, SHA-1 ou SHA-256 sans sel pour les mots de passeRetrouvés en masse par tables ou force bruteFonction lente et salée
Clé écrite dans le code ou stockée avec les donnéesLe chiffrement ne protège plus rienCoffre-fort de secrets, KMS
IV fixe ou réutiliséRévèle des informations sur les donnéesIV aléatoire à chaque chiffrement
Mode ECBDeux blocs identiques donnent le même chiffré : les motifs restent visiblesAES-GCM
Aléatoire prévisible pour un jetonJetons devinablesGénérateur cryptographique
Pages sensibles en HTTP, TLS 1.0Interception des données et des cookiesHTTPS partout, TLS 1.2 ou 1.3, HSTS
Algorithme « maison »Personne ne l'a analysé : il a des faillesBibliothèques éprouvées

Ce chapitre résume ce que la sécurité applicative attend de la cryptographie. Pour le fonctionnement interne des algorithmes et des certificats, appuie-toi sur le cours Cybersécurité des infrastructures.

Exercice 1 sur 4 Hacher ou chiffrer ? Réussi À revoir

Pour chaque donnée d'AppliFrais, choisis : hachage lent et salé, chiffrement symétrique, empreinte SHA-256, ou rien.

  1. Le mot de passe d'un visiteur.
  2. L'IBAN d'un visiteur, utilisé chaque mois pour le virement.
  3. Un justificatif PDF dont on veut prouver qu'il n'a pas été modifié depuis son envoi.
  4. Le libellé d'un frais (« Hôtel Ibis Lyon »).
  5. Le jeton de réinitialisation de mot de passe, stocké en base pendant 30 minutes.
Voir la réponseMasquer la réponse de l'exercice 1
  1. Hachage lent et salé (Argon2id ou bcrypt) : on n'a jamais besoin de le relire, seulement de le vérifier.
  2. Chiffrement symétrique (AES-256-GCM) : il faut le relire pour payer ; la clé est stockée hors de la base.
  3. Empreinte SHA-256 du fichier, calculée à l'envoi et stockée ; on la recalcule pour vérifier l'intégrité. Pour prouver aussi qui a produit le document, on utiliserait une signature.
  4. Rien de particulier : c'est une donnée personnelle ordinaire, protégée par le contrôle d'accès et le chiffrement du disque et des sauvegardes.
  5. Empreinte du jeton (SHA-256 suffit ici, car le jeton est long et aléatoire, donc impossible à deviner par force brute) : si la base fuite, les jetons en cours ne sont pas utilisables. Le jeton en clair n'existe que dans le lien envoyé par mail.

Tu avais trouvé ?

Exercice 2 sur 4 Ordres de grandeur Réussi À revoir

Un attaquant a volé une base où les mots de passe sont hachés en MD5 sans sel. Il teste 10¹⁰ empreintes par seconde.

  1. Combien de temps lui faut-il pour essayer tous les codes PIN à 6 chiffres ?
  2. Et tous les mots de passe de 8 lettres minuscules (26 lettres) ?
  3. Le même mot de passe de 8 minuscules est maintenant haché avec bcrypt ; l'attaquant ne teste plus que 10⁴ empreintes par seconde. Combien de temps lui faut-il ?
  4. Que montrent ces résultats sur le choix de l'algorithme et sur la longueur des mots de passe ?
Voir la réponseMasquer la réponse de l'exercice 2
  1. 10⁶ combinaisons ÷ 10¹⁰ par seconde = 10⁻⁴ seconde : instantané.
  2. 26⁸ ≈ 2,09 × 10¹¹ combinaisons ÷ 10¹⁰ = environ 21 secondes.
  3. 2,09 × 10¹¹ ÷ 10⁴ ≈ 2,09 × 10⁷ secondes, soit environ 242 jours.
  4. L'algorithme lent multiplie le temps d'attaque par un million : le même mot de passe passe de 21 secondes à huit mois. Mais un mot de passe court et pauvre reste à la portée d'un attaquant patient : il faut les deux, une fonction lente et des mots de passe longs (politique de mots de passe).

Tu avais trouvé ?

Exercice 3 sur 4 Corriger un stockage de mots de passe Réussi À revoir

Une application Node.js enregistre les comptes ainsi :

js
const hash = crypto.createHash('md5').update(req.body.mdp).digest('hex');
await db.execute('INSERT INTO utilisateur (login, mdp_hash) VALUES (?, ?)', [req.body.login, hash]);
  1. Cite deux défauts de ce stockage.
  2. Réécris ces deux lignes avec Argon2id.
  3. La base contient déjà 300 comptes en MD5. Comment migrer sans demander à tout le monde de changer de mot de passe ?
Voir la réponseMasquer la réponse de l'exercice 3
  1. MD5 est rapide (des milliards d'essais par seconde) et utilisé sans sel : deux mots de passe identiques ont la même empreinte, retrouvable dans des tables précalculées. MD5 est par ailleurs cassé pour les usages cryptographiques.
  2. Avec le paquet argon2 :
js
const hash = await argon2.hash(req.body.mdp);
await db.execute('INSERT INTO utilisateur (login, mdp_hash) VALUES (?, ?)', [req.body.login, hash]);
  1. À la prochaine connexion réussie de chaque utilisateur, on vérifie le mot de passe saisi contre l'ancienne empreinte MD5, puis on calcule et on enregistre son empreinte Argon2id. Les comptes qui ne se reconnectent pas pendant une période fixée sont désactivés et devront réinitialiser leur mot de passe. Une variante consiste à hacher tout de suite les empreintes MD5 existantes avec Argon2id (Argon2id(MD5(mdp))) pour ne plus laisser de MD5 nu en base.

Tu avais trouvé ?

Exercice 4 sur 4 Repérer l'erreur Réussi À revoir

Pour chaque extrait, nomme l'erreur cryptographique.

  1. const jeton = Math.random().toString(36).slice(2); pour un lien de réinitialisation.
  2. const iv = Buffer.alloc(12, 0); avant chaque appel à createCipheriv('aes-256-gcm', …).
  3. La clé AES est stockée dans une table parametre de la même base que les IBAN chiffrés.
  4. Le formulaire de connexion est servi en HTTPS, mais toutes les autres pages en HTTP.
Voir la réponseMasquer la réponse de l'exercice 4
  1. Aléatoire prévisible : Math.random() n'est pas un générateur cryptographique. Utiliser crypto.randomBytes(32).toString('hex').
  2. IV fixe, réutilisé à chaque chiffrement : avec GCM, c'est une faille grave. Tirer un IV avec crypto.randomBytes(12) à chaque fois et le stocker avec le chiffré.
  3. Clé stockée avec les données : qui vole la base vole aussi la clé. La placer dans un coffre-fort de secrets ou une variable d'environnement du serveur, avec un accès restreint.
  4. HTTPS partiel : le cookie de session circule en clair sur les autres pages et peut être intercepté. HTTPS sur tout le site, cookie Secure et en-tête HSTS.

Tu avais trouvé ?

Pour aller plus loin. Le cours Cryptographie reprend chaque outil en profondeur : chiffrement symétrique, fonction de hachage, stockage des mots de passe et TLS 1.3.

Chapitre terminé !

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

chapitres
–
de série
–
du cours
–
Chapitre suivant : Injections (A05) : SQL, NoSQL et commandes systèmeRevoir le sommaire du cours