Comment sont gardés les mots de passe
La pire erreur : le mot de passe en clair
Quand tu crées un compte, le site doit pouvoir vérifier ton mot de passe à chaque connexion. La question clé : comment le range-t-il ?
La pire réponse, hélas encore vue sur de vrais sites : tel quel, en clair, dans la base de données.
utilisateur | mot_de_passe
awa | soleil
paul | Douala2024
Le problème : le jour où quelqu'un vole la base (par une injection SQL, par exemple, vue dans le module précédent), il a tous les mots de passe de tous les comptes, immédiatement. Et comme les gens réutilisent le même mot de passe partout, il a aussi leurs comptes mail, bancaires, Mobile Money.
Le hachage : une porte à sens unique
Tu as déjà croisé le hachage dans le module dictionnaires. On va l'utiliser pour de vrai.
C'est comme un hachoir à viande : on transforme un steak en viande hachée en un instant, mais personne ne peut reconstituer le steak à partir de la viande hachée.
Python fournit les fonctions de hachage dans la bibliothèque standard hashlib (rien à installer) :
import hashlib
empreinte = hashlib.sha256("soleil".encode()).hexdigest()
print(empreinte)
# e93e24c1b877a15df5e4a8cbb20f351b891b750779cf5e8fb99119a7142d89cf
sha256est une fonction de hachage très répandue (256 bits) ;.encode()transforme le texte en octets (le hachage travaille sur des octets) ;.hexdigest()donne l'empreinte en hexadécimal, 64 caractères.
Voir le hachage en action
Lance, puis change les mots. Remarque : la plus petite modification change toute l'empreinte (c'est l'effet avalanche), et le même mot redonne toujours la même empreinte.
import hashlib
def empreinte(texte):
return hashlib.sha256(texte.encode()).hexdigest()
print(empreinte("soleil"))
print(empreinte("Soleil")) # une majuscule : tout change
print(empreinte("soleil")) # le meme mot : la meme empreinte
print(empreinte("")) # meme le vide a une empreinte
Vérifier sans connaître
Voici l'astuce qui permet à un site de vérifier ton mot de passe sans le connaître :
- À l'inscription, le site calcule l'empreinte de ton mot de passe et range l'empreinte (jamais le mot de passe).
- À la connexion, il calcule l'empreinte de ce que tu tapes, et la compare à celle qu'il a rangée.
- Si les deux empreintes sont identiques, c'est que tu as tapé le bon mot de passe.
utilisateur | empreinte
awa | e93e24c1b877a15df5e4a8cbb20f351b891b...
Si un voleur prend cette base, il a les empreintes, pas les mots de passe. Et comme le hachage est à sens unique, il ne peut pas remonter aux mots de passe... en théorie. On verra à la leçon suivante que ce n'est pas si simple, et ce qu'il faut ajouter.
Exercice : Un système de connexion correct
Écris deux fonctions :
inscrire(mot_de_passe): renvoie l'empreinte SHA-256 du mot de passe (c'est ce que le site rangerait) ;verifier(mot_de_passe, empreinte_rangee): renvoieTruesi le mot de passe correspond à l'empreinte rangée,Falsesinon.
Voir une solution
import hashlib
def inscrire(mot_de_passe):
"""Renvoie l'empreinte SHA-256 à ranger dans la base."""
return hashlib.sha256(mot_de_passe.encode()).hexdigest()
def verifier(mot_de_passe, empreinte_rangee):
"""Renvoie True si le mot de passe correspond à l'empreinte rangée."""
return inscrire(mot_de_passe) == empreinte_rangee
emp = inscrire("soleil")
print(emp)
print(verifier("soleil", emp))
print(verifier("Soleil", emp))Dans l'appli, ton code est exécuté et corrigé automatiquement. Faire l'exercice
Le sel : contre les tables arc-en-ciel
Deux personnes qui choisissent le même mot de passe (123456) auront la même empreinte. Un attaquant peut donc préparer à l'avance une table géante « empreinte → mot de passe » pour tous les mots de passe courants, et retrouver instantanément qui utilise 123456.
La parade : le sel.
Résultat : deux personnes avec le mot de passe 123456 ont des sels différents, donc des empreintes différentes. Les tables arc-en-ciel deviennent inutiles : il faudrait en refaire une par sel.
import hashlib, os
sel = os.urandom(16) # 16 octets aléatoires, un par utilisateur
empreinte = hashlib.pbkdf2_hmac("sha256", "soleil".encode(), sel, 100000)
100000 est le nombre de répétitions : plus c'est grand, plus c'est lent à attaquer.
Exercice : Hacher avec du sel
Écris hacher_sale(mot_de_passe, sel) qui renvoie l'empreinte pbkdf2 (SHA-256, 100 000 répétitions) du mot de passe avec le sel donné, en hexadécimal.
Le sel est fourni en octets. pbkdf2_hmac renvoie des octets ; transforme-les en hexadécimal avec .hex().
Vérifie ensuite que le même mot de passe avec deux sels différents donne deux empreintes différentes.
Voir une solution
import hashlib
def hacher_sale(mot_de_passe, sel):
"""Empreinte pbkdf2-sha256 (100000 tours) du mot de passe salé, en hexadécimal."""
return hashlib.pbkdf2_hmac("sha256", mot_de_passe.encode(), sel, 100000).hex()
sel_a = b"sel-de-awa-00001"
sel_b = b"sel-de-paul-0002"
print(hacher_sale("123456", sel_a))
print(hacher_sale("123456", sel_b))Dans l'appli, ton code est exécuté et corrigé automatiquement. Faire l'exercice