Comment protéger un mot de passe stocké
Un site ne doit jamais garder les mots de passe tels quels. Il garde une empreinte, appelée hachage, d'où l'on ne peut pas retrouver le mot de passe. À la connexion, il compare les empreintes.
C'est important, car une base de données finit parfois par fuir. Avec des empreintes bien faites, la fuite ne livre pas les mots de passe des clients, qui servent souvent ailleurs.
À la fin de cette leçon
Remarque
Je peux stocker un mot de passe sous forme d'empreinte salée et vérifier qu'une session est encore valable.
Mots-clés
| Mot | Ce que ça veut dire | Exemple |
|---|---|---|
| Hachage | un calcul à sens unique qui transforme un texte en une empreinte de longueur fixe | |
| Sel | un texte aléatoire ajouté au mot de passe avant le hachage, propre à chaque compte | |
| Hachage lent | un hachage voulu long à calculer, pour décourager les essais en masse | pbkdf2 |
| Session | le lien entre un navigateur et un compte connecté, tenu par un jeton | |
| Jeton de session | un long texte aléatoire donné au navigateur après la connexion | |
| Expiration | le moment où une session cesse d'être valable |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
La règle unique
Attention
Ce laboratoire est une boutique volontairement fragile. Regardez votre copie privée, et rien d'autre : elle n'a aucune sortie vers Internet. Tester un système qui n'est pas le vôtre, ou sans permission, est un délit dans presque tous les pays.
Un hachage ne se défait pas
Un hachage transforme un texte en une suite de lettres et de chiffres, l'empreinte. Le même texte donne toujours la même empreinte. Un texte à peine différent en donne une toute autre.
Le calcul ne se défait pas : de l'empreinte, on ne retrouve pas le texte. On peut seulement essayer des textes jusqu'à retomber sur la même empreinte.
Deux mots presque identiques, deux empreintes sans rapport
Voyez-le avec la commande sha256sum, qui calcule une empreinte. La première commande hache motdepasse. La seconde hache Motdepasse, avec une seule majuscule de plus.
Chacune affiche 64 signes suivis de deux espaces et d'un tiret. Comparez le début des deux lignes : rien ne se ressemble.
L'empreinte de motdepasse
printf 'motdepasse' | sha256sumAffichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
L'empreinte de Motdepasse
printf 'Motdepasse' | sha256sumAffichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Le sel protège les comptes identiques
Deux clients qui choisissent le même mot de passe auraient la même empreinte. Un attaquant qui voit la base le remarque, et en casse deux d'un seul essai.
Le sel règle cela : on ajoute au mot de passe un texte aléatoire propre à chaque compte avant le hachage. Les deux empreintes diffèrent. Le sel se range à côté de l'empreinte, car il n'est pas secret.
Un hachage lent décourage les essais en masse
Un attaquant qui a la base essaie des millions de textes par seconde. Un hachage ordinaire est trop rapide pour l'arrêter. On choisit donc un hachage lent, qui répète son calcul des milliers de fois.
Une connexion d'un client coûte alors une fraction de seconde. Un million d'essais coûte des heures. Python propose hashlib.pbkdf2_hmac, et on ne l'écrit jamais soi-même.
Saler et hacher un mot de passe
import hashlib
import secrets
# Le sel est tiré au hasard, propre à chaque compte.
sel = secrets.token_hex(16)
mot_de_passe = "bleu-citron-42"
empreinte = hashlib.pbkdf2_hmac("sha256", mot_de_passe.encode(), sel.encode(), 200000).hex()
print("sel :", len(sel), "signes")
print("empreinte :", len(empreinte), "signes")
# On range sel et empreinte, jamais le mot de passe.
verification = hashlib.pbkdf2_hmac("sha256", "bleu-citron-42".encode(), sel.encode(), 200000).hex()
print("même mot de passe, même empreinte :", verification == empreinte)
Le laboratoire garde-t-il les mots de passe correctement ?
Regardons comment la boutique de votre laboratoire crée sa table de comptes. La commande suivante affiche la ligne du programme qui la crée, sans rien lire dans la table.
Elle montre une colonne nommée password de type texte. Le programme y range les mots de passe tels quels, sans empreinte ni sel. C'est le contraire de ce que vous venez d'apprendre.
La ligne qui crée la table des comptes
docker exec app grep -n "password TEXT" /tmp/app/app.pyAffichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Un jeton de session est imprévisible et bref
Après la connexion, le navigateur reçoit un jeton : sans lui, il faudrait redonner le mot de passe à chaque page. Si quelqu'un devine ou vole le jeton, il devient ce client.
Le jeton est donc long, tiré au hasard par une fonction faite pour la sécurité, comme secrets.token_hex. Il expire, après une durée totale et après un temps sans activité.
Exemple travaillé : trois sessions à juger
| Session | Âge | Dernière action | Verdict |
|---|---|---|---|
| Jeton de 40 signes | 2 heures | il y a 5 minutes | Valable |
| Jeton de 40 signes | 9 heures | il y a 5 minutes | Expirée : plus de 8 heures au total |
| Jeton de 40 signes | 2 heures | il y a 45 minutes | Inactive : plus de 30 minutes sans action |
| Jeton de 6 signes | 1 minute | à l'instant | Refusée : jeton trop court |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Une erreur fréquente
Attention
Beaucoup pensent qu'un mot de passe « chiffré » est protégé. Un chiffrement se défait avec une clé, et la clé finit souvent à côté des données. Un mot de passe ne doit pas pouvoir être retrouvé : on garde une empreinte salée et lente, jamais un texte chiffré.