Mots de passes hachés ? Salés ??? (en anglais: hashed, salted)

Posted by

Les études en cryptographie sont très poussées et nombreuses. Ce n’est pas le but de cet article.

Imaginons que je crée un site web où tous les utilisateurs doivent s’enregistrer, fournir une adresse e-mail et un mot de passe de leur choix.

Si mon site se fait pirater et que ma liste d’utilisateurs est dérobée, je cours une grande responsabilité vis-à-vis de tous ceux qui m’ont confié leur identité et un mot de passe de leur choix. N’oublions pas que madame Michu utilise toujours le même mot de passe partout car c’est pour elle le seul moyen de ne pas l’oublier !

100% stupide: les mots de passe sont enregistrés tels quels dans la liste d’utilisateurs.

99,9% stupide: on va les transformer sous une forme évidente, par exemple en transformant Password en drowssaP ou bien en Qbttxpse. (Avez-vous trouvé ?)

C’est ici qu’interviennent les algorithmes de hashage (du moins bon au meilleur: MD4, MD5, SHA1, SHA2). Ces algorithmes transforment une donnée de n’importe quelle longueur en un résultat de longueur fixe sans aucune corrélation visible.

Voici quelques exemples avec MD5 :

a -> 0cc175b9c0f1b6a831c399e269772661
b -> 92eb5ffee6ae2fec3ad71c777531578f
aa -> 4124bc0a9335c27f086f24ba207a4912
bb -> 21ad0bd836b90d08f4cf640b4c298e7c
?? -> 5f4dcc3b5aa765d61d8327deb882cf99

et ainsi de suite. Mais au fait, que vaut le ?? ci-dessus. Facile, demandez 5f4dcc3b5aa765d61d8327deb882cf99 à Google.

Un bon hash interdit toute collision, de telle manière qu’il soit impossible de trouver deux données différentes qui donnent le même hash. Il doit être imprévisible de savoir ce qui a changé dans la donnée si le hash change, et aucun renseignement ne doit filtrer dans aucun sens. Le hash n’indique pas la longueur de la chaîne d’entrée, ni aucun autre renseignement.

On va se croire un peu plus malin, et enregistrer les hash des mots de passe dans le fichier d’utilisateurs. Quand la personne se connecte et présente son mot de passe, on le hashe, on compare les hash, s’ils diffèrent, le mot de passe présenté est faux.

C’est ce qui est arrivé chez Yahoo qui s’est fait pirater en 2013, et s’est fait dérober un demi-milliard ou deux milliards de comptes, peu importe le nombre exact. Les mots de passe étaient bêtement stockés sous forme hashée MD5.

En fait ça n’offre aucune protection, puisqu’il existe des listes avec des centaines de milliers de mots, courts et longs, figurant ou non dans les dictionnaires, avec leur hash correspondant. Voyez par exemple cette page https://md5db.net/explore/B408 d’un site qui contient 65000 autres pages similaires, toutes indexées par Google et autres outils de recherche.

Si on prend un mot de passe ne figurant dans aucun dictionnaire, et dont le hash est  c53e479b03b3220d3d56da88c4cace20, croyez-vous être plus sûr ? Rien n’est moins sûr car à nouveau quelqu’un y a pensé avant vous ! (L’exemple est le hash de P@$$w0rd)

C’est ici qu’on ajoute du sel. Le sel est un nombre aléatoire, vraiment aléatoire et suffisamment long qu’on va inclure dans le processus.

Pour l’exemple disons que le sel est un nombre entre 0000 et 9999. Au hasard 2016, et le mot de passe “aa”.

md5("aa") -> 4124bc0a9335c27f086f24ba207a4912

Ajoutons du sel et on hache une deuxième fois
md5("20164124bc0a9335c27f086f24ba207a4912")
   -> 66863812d410807765bac7856ef334c2

Et dans ce cas-ci, même pour un mot de passe aussi pauvre que “aa”, Google ne trouve plus aucun résultat pour le hash 66863812d410807765bac7856ef334c2 que nous venons de produire.

Saler les hash est aussi très important: si deux utilisateurs utilisent le même mot de passe, grâce au sel aléatoire, leurs mots de passe hachés n’auront aucune ressemblance.

Je n’ai plus qu’à enregister dans mon fichier d’utilisateurs le sel (2016) et le hash final (66863812d410807765bac7856ef334c2) ; au moment où l’utilisateur se connecte on refait le même processus de hashage, mais on connaît la valeur à utiliser pour le sel et reproduire le même hashage pour vérifier si le mot de passe est correct.

En réalité on prend des valeurs beaucoup plus longues et complexes pour le sel et pour le hashage.

Dans un système Unix/Linux, les mots de passe sont stockés dans un fichier /etc/shadow .

root:~# adduser exemple
Adding user `exemple' ...
...
Enter new UNIX password: secret
Retype new UNIX password: secret
...
Is the information correct? [Y/n]


root:~# grep exemple /etc/passwd /etc/shadow
/etc/passwd:exemple:x:1002:1002:,,,:/home/exemple:/bin/bash
/etc/shadow:exemple:$6$ztDVi3xQ$2KXD9LWl36eVah8lvEpiDBi.Y5SaCuzqr8bFXRNMPPHtxZ5wzOdTLOeB3tDgPZ4zR9lVmEA0e9PAT/1tjfjRP0:17205:0:99999:7:::

Le signe “$” délimite les champs. Par convention $6$ indique qu’on va hasher avec sha-512, un successeur de MD5 plus robuste, et que les champs séparés par “$” sont le sel et ensuite le mot de passe hashé.

On voit aussi que les chaînes de caractères ne sont pas constituées d’hexadécimal (16 catactères possibles, de 0 à 9 et de A à F) mais de base64 (64 caractères possibles de 0 à 9, de A à Z, de a à z, ainsi que / et .) .

Le sel est ici une enfilade de 8 caractères. Ce qui fait 64 ^8 possibilités, soit 281474976710656, ou 281000 milliards de possibilités. En 2016, personne n’oserait imaginer d’enregistrer toutes les 281000 milliards de variantes hashées, de tous les mots du grand Larousse.

Vérifions mon mot de passe ci-dessous:

root:~# mkpasswd -m sha-512 secret ztDVi3xQ
$6$ztDVi3xQ$2KXD9LWl36eVah8lvEpiDBi.Y5SaCuzqr8bFXRNMPPHtxZ5wzOdTLOeB3tDgPZ4zR9lVmEA0e9PAT/1tjfjRP0