ParcoursOuvrir l'appli

Comment sont gardés les mots de passe

Sécurité et hacking éthique · 50 min

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

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 :

  1. À l'inscription, le site calcule l'empreinte de ton mot de passe et range l'empreinte (jamais le mot de passe).
  2. À la connexion, il calcule l'empreinte de ce que tu tapes, et la compare à celle qu'il a rangée.
  3. 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 :

  1. inscrire(mot_de_passe) : renvoie l'empreinte SHA-256 du mot de passe (c'est ce que le site rangerait) ;
  2. verifier(mot_de_passe, empreinte_rangee) : renvoie True si le mot de passe correspond à l'empreinte rangée, False sinon.
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