Stocker mots de passe database

Stocker les mots de passe de manière sécurisée est une préoccupation récurrente.
Mais quelles sont les principales méthodes, comment fonctionnent-elles, et que valent-elles face aux techniques actuelles de cassage de mots de passe ? 
Nous vous expliquons dans cet article les principes essentiels d’un stockage sécurisé (hash, sel, poivre, itération) et mettrons en évidence leur importance pour résister aux méthodes de récupération des mots de passe. Enfin, nous vous parlerons d’une fonction de hashage fiable pour un stockage sécurisé.

Mots de passe en clair

Admettons qu’un attaquant arrive à récupérer une base de données d’une application Web et en extrait de la base le couple <Login, Mot de passe>. 

LoginMot de passe
adminazerty
totomatrix
billyyep59f$4txwrr
tatamatrix
titifreepass
attaquanttesPwndPassword

Dans ce cas, l’attaquant possède directement les mots de passe en clair de tous les utilisateurs. Même Billy qui possède un mot de passe robuste n’est pas protégé.

Stocker les mots de passe en clair n’est PAS une solution sécurisée. Personne, y compris les administrateurs du site/ base de données, ne doit avoir accès au mot de passe en clair de l’utilisateur.

Mots de passe chiffrés

Dans certains cas, les mots de passe sont stockés en base de données après avoir été chiffrés par un algorithme réversible (rot13, chiffrement par masque…). Comme l’algorithme est réversible, celui-ci ne respecte pas les règles de la CNIL.

En effet, elle recommande que tout mot de passe soit transformé au moyen d’une fonction cryptographique non réversible. (source)

De plus, l’attaquant ayant connaissance de son mot de passe en clair/chiffré, il peut deviner la logique du chiffrement et tenter de l’inverser. S’il y arrive, tous les mots de passe seront récupérés presque aussi rapidement que s’ils étaient en clair, indépendamment de la complexité de l’algorithme.

Fonctions de hash obsolètes 

Dans de nombreux cas, les mots de passe sont stockés avec des fonctions cryptographiques irréversibles dépassées (md5, sha1…). Par exemple, le site LinkedIn stockait une partie de ses mots de passe avec du sha1, et après la fuite de ces hashs en 2012, il n’a fallu que trois jours pour récupérer 90% des mots de passe.

Imaginons la table de donnée suivante (les mots de passe sont les mêmes que précédemment)

LoginMot de passe (md5)
adminab4f63f9ac65152575886860dde480a1
toto21b72c0b7adc5c7b4a50ffcb90d92dd6
billy47ad898a379c3dad10b4812eba843601
tata21b72c0b7adc5c7b4a50ffcb90d92dd6
titi5b9a8069d33fe9812dc8310ebff0a315

Note : 

  • Dans notre cas, tous les mots de passe (sauf celui de Billy) sont des mots de passe très fréquemment utilisés et se retrouvent dans les mots de passe les plus utilisés. 
    (par exemple dans la liste 10-million-password-list-top-1000.txt)
  • Il est intéressant aussi de remarquer que les hashs n’ayant pas de notion d’aléatoire, toto et tata partagent le même hash, car ils ont le même mot de passe.

Une simple recherche du hash de l’admin sur internet permet de retrouver directement son mot de passe.

Searching md5 hash in google

Sauf pour l’utilisateur Billy qui a un mot de passe complexe, il est possible de retrouver tous les hashs directement par des requêtes dans un moteur de recherche.

Si les hashs ne sont pas retrouvés directement sur un moteur de recherche, l’attaquant dispose d’autres méthodes :

Brute force

Le brute force est l’action d’essayer toutes les possibilités de façon itérative en suivant une règle de génération. C’est comme quand nous essayons d’ouvrir un cadenas à code en énumérant toutes les possibilités de 0000 à 9999 jusqu’à l’ouverture de celui-ci. 

Dictionnaire

Une attaque par dictionnaire est une attaque où l’on essaie tous les termes présents dans une liste de mots. Plusieurs types de dictionnaires peuvent être imaginés :

  • Un dictionnaire de langue
  • Un classement des mots de passe les plus utilisés 
  • Une liste adaptée à un contexte donné

Si l’on reprend l’image du cadenas, nous pouvons imaginer une liste contextualisée de cette façon : 

On sait que le cadenas appartient à « Tutu », qu’il adore le nombre 42, et qu’il est né le 26 novembre 2001.  Nous pouvons donc supposer que le cadenas peut possiblement contenir le nombre 42, 26, 11 et 01 et donc générer une liste en fonction de ces critères. 

Remarque : Par abus de langage, il est courant d’appeler une attaque par dictionnaire une attaque par brute force. 

Rainbow table

Les rainbow table sont un sujet qui mérite à lui seul un article. Rapidement, c’est une structure de données qui permet de retrouver des mots de passe avec un bon compromis stockage/temps. Cette structure a une liste de hashs précalculés et permet de retrouver un hash en un temps acceptable. 
De nombreuses rainbow table sont disponibles en ligne.

Benchmark pour récupérer les mots de passe md5

Un benchmark a été effectué sur les hashs de mots de passe de notre article avec la liste rockyou, qui contient 14,341,564 mots de passe unique. Le benchmark a été effectué sur une machine virtuelle, ce qui n’est pas optimal pour casser des mots de passe.

Rockyou list

En regardant les résultats, nous voyons qu’à l’exception du mot de passe de Billy, qui n’est pas dans la liste rockyou, les trois mots de passe ont été trouvés et qu’il a fallu 11 secondes pour que le logiciel calcule tous les hashs présents dans rockyou. 

Fonctions de hash inadaptées

Après avoir vu les mauvais exemples précédents, il est tentant d’utiliser des fonctions irréversibles sécurisées comme sha256, sha512, ou bien sha3. Cependant, le but de ces fonctions est de servir à calculer un résumé cryptographique pour contrôler l’intégrité d’un fichier, faire une signature électronique ou bien optimiser la recherche et l’indexation. Elles ne sont pas adaptées pour le stockage de mot de passe, car elles sont rapides à calculer comme le prouve le brenchmark suivant :

LoginMot de passe (sha512)
admindf6b9fb15cfdbb7527be5a8a6e39f39e572c8ddb943fbc79a943438e9d3d85ebfc2ccf9e0eccd9346026c0b6876e0e01556fe56f135582c05fbdbb505d46755a
toto11a25e88658143a853d280bf77f81ff391347aaba2db54a3aab0149b265276de419880762a473fc496388bcf70566d7cfd0346c34add40652f8f7b669caf9ec0
billyfe9cb9b07725fd1cc3906232405119fedf9a020436630d3c1e0f632f73909e6ed9e731c149ac22743bbe9541881f35ceebf1d2782d469eb3b42968469d55a7a4
tata11a25e88658143a853d280bf77f81ff391347aaba2db54a3aab0149b265276de419880762a473fc496388bcf70566d7cfd0346c34add40652f8f7b669caf9ec0
titif767036acd951f5ddaf4eed5291c677db060055806dbcae69ca35d95847559dc8abce5011fd2b50833e760eca2d84d6daf1f078200f42b4fc10b58bad3761c88
Hashcat

Là encore, sauf pour l’utilisateur Billy, tous les mots de passe ont été retrouvés et il n’a fallu que 16 secondes pour que hashcat finisse ses calculs. 

Améliorer SHA512

Même s’il a été dit précédemment que la fonction SHA512 n’était pas optimisée pour le stockage de mot de passe, il peut être intéressant de montrer comment optimiser celle-ci pour comprendre l’intérêt des fonctions de hash adaptées pour les mots de passe. 

Utilisation d’un sel

Le sel est un polluant à la donnée brute (ici le mot de passe) permettant de produire deux hashs différents à partir d’une même donnée.  Le sel est unique pour chaque utilisateur et il est composé d’une suite aléatoire.  Il augmente la chance d’unicité d’un mot de passe et donc la chance qu’un hash n’ait jamais été utilisé.
Par exemple, avec du sel, toto et tata n’auront pas le même hash dans la base de données.

Les avantages du sel sont multiples :

  1. Il est quasiment impossible de trouver le hash directement sur internet si celui-ci est salé. Cependant, le sel doit être assez long et aléatoire.
  2. Les rainbow tables ne fonctionnent pas avec des hash salés
  3. Comme dit précédemment, deux utilisateurs ayant le même mot de passe n’auront pas le même hash si du sel est utilisé. Les logiciels de cassage de mot de passe (hashcat, Johntheripper…), après avoir cassé un hash, regardent si celui-ci n’est pas présent pour un autre utilisateur. Ainsi, sans sel, après avoir découvert le mot de passe de toto, celui de tata est directement découvert. En revanche, avec un sel, le logiciel doit recommencer à zéro pour chaque utilisateur. 

Benchmark avec sel

UtilisateurSelHash sha512 avec sel
adminBGdd6d6^ZgvkMhKf@W3RqT7509d123bce1aa92331861cf8fd738a58205045123f0e25f0862477cb19d3ee0757cd99865c30b123ad1e7f1be1e31a6058090458cb9941031f5c36683c8446e
totoHZBD^@gL*wvoExo6yJ7hVB6b28830776de6ad7ef1dd8c221e0d53fec4532c623075d0216d937ab82ab284a56a461ce5d4ec77d1783665a262a6a1eb98627b1f6260da55dbb782d7cb75bc4
billywvVndjwcZJy!dwT4fBD@U^2847b2605f6a1cd88399e6c9784c0e583799be9485cb128fe5f541f43636559067ec32de33e9b3fa2c15b15eec294cf262fd7aab2395dd64d6dbd9640b4fe6fd
tataQeNWm9NXqJ8m@m2^F7Kh9*165bc06b69fa2bfcd893bfde86358394406c87c7f7abba891cd10ed9fac887c54d52ed14310ad675078033e9bca80084d345fb2836933e55c60f734982430e2b
titiiQUemgw9M6Gw*&v6RG%MZ#f8eded6c815c7522ab6197aa319d3ff4cddc2c7eeffa0f91c1291603f807a47f320324d2ce2fed1fb3cbfe19524fc5d9c105093f755d76a949efb212fb85c942

Avec cette configuration, il a fallu 33 secondes au logiciel hashcat pour récupérer les mots de passe. Aucun des hashs présents dans la base de données n’est indexé par un moteur de recherche.

Utilisation d’un poivre

Le poivre est aussi un polluant, mais commun à tous les utilisateurs. Celui-ci n’est pas stocké en base de données, mais dans les sources d’une application, dans un fichier de configuration ou en variable d’environnement. Un attaquant ayant « juste » compromis une base de données doit deviner le poivre ou bien le récupérer d’une autre manière pour pouvoir casser efficacement les hashs.

Augmenter le nombre d’itérations

Une autre manière de renforcer la sécurité est de répéter le nombre d’itérations du hash. Augmenter le nombre d’itérations signifie que nous allons hasher plusieurs fois le mot de passe. Par exemple, avec du sha512 nous avons la boucle suivante :

Tant qu’itération est supérieur à 0
hash = sha512(hash)
Décrémenter itération

Pour un utilisateur qui s’identifie, le calcul du hash sera plus long (on reste quand même dans l’ordre de la milliseconde). Mais là où un utilisateur perd quelques millisecondes pour se connecter, un attaquant va perdre beaucoup plus de temps, car celui-ci va perdre plusieurs millisecondes par tentatives et comme celui-ci en effectue des millions, cela va se traduire par des heures/jours en plus pour récupérer les mots de passe.

Fusion des 3 méthodes

Nous pouvons fusionner les trois méthodes (sel, poivre et nombre d’itérations) pour avoir une méthode pour stocker les mots de passe de manière plus sécurisée qu’un simple hash.

Fonction calcul_hash(password,salt,pepper,itération)
Inputs
password est le mot de passe de l’utilisateur en clair
salt est le sel unique par utilisateur et est généré aléatoirement
pepper est le poivre commun à tous les utilisateurs et est généré aléatoirement
iteration est le nombre d’itérations

Output :
Le hash du mot de passe
Hash = sha512(salt+password+pepper)
Tant qu’itération est supérieur à 0
hash = sha512(hash)
Décrémenter itération

return hash

Ensuite, pour vérifier lors de la connexion les mots de passe, il suffit d’appeler la même fonction avec le mot de passe saisi par l’utilisateur et de le comparer avec le hash dans la base de données. Si les deux sont identiques, alors la connexion est réussie.

Utilisation de fonctions spécifiques

Précédemment, nous avons réussi à créer un algorithme permettant de générer des hashs plus résistants aux logiciels de cassage de mots de passe. Cependant, des fonctions existent déjà et ont prouvé leur efficacité dans le temps. Il est donc inutile de réinventer la roue au risque de provoquer des erreurs. Parmi ces différences fonctions, nous pouvons trouver : Argon2, scrypt, PBKDF2, bcrypt…

Ces fonctions présentent de nombreux points forts :

  • Plus lentes à calculer
  • Plus gourmandes en accès RAM (ce qui est le point faible des GPU)
  • Définition du nombre d’itérations de la fonction cryptographique utilisée. Comme vu précédemment, plus il y en a, plus le calcul sera couteux.

Bcrypt

bcrypt est une fonction de hachage créée par Niels Provos et David Mazières. Elle est basée sur l’algorithme de chiffrement Blowfish et a été présentée lors de USENIX en 1999. 

Parmi ces points positifs en plus de ceux cités précédemment, nous trouvons des implémentations dans beaucoup de langages. De plus, cet algorithme datant de 1999, il a montré sa solidité dans le temps, là où certains algorithmes comme Argon2(i) n’existent que depuis 2015.

Le hash calculé par bcrypt a une forme prédéfinie :

$2y$11$SXAXZyioy60hbnymeoJ9.ulscXwUFMhbvLaTxAt729tGusw.5AG4C

  1. Algorithme : Celui-ci peut prendre plusieurs versions en fonction de la version de bcrypt ($2$, $2a$, $2x$, $2y$ et $2b$)
  2. Le coût :  Le nombre d’itérations en puissance de 2. Par exemple ici l’itération est à 11, l’algorithme va faire 211 itérations (2048 itérations)
  3. Le sel : Au lieu de stocker le sel dans une colonne dédiée, celui-ci est directement stocké dans le hash final
  4. Le mot de passe hashé

Comme bcrypt stocke le nombre d’itérations, ceci fait de lui une fonction adaptative, car on peut augmenter le nombre d’itérations et donc celle-ci est de plus en plus longue. Cela lui permet, malgré son âge et l’évolution de la puissance de calcul, d’être toujours robuste face aux attaques de brute force. Le brenchmark suivant montre qu’il faut 23 jours à hashcat pour calculer la totalité des hashs de rockyou.

Bcrypt

Il est intéressant de noter que les mots de passe azerty et matrix, étant des mots de passe très faibles et présents en début de liste, ont été trouvés durant le court temps (2 heures) où le logiciel a fonctionné dans l’exemple.

Conclusion 

Nous avons pu voir dans cet article l’utilité d’une fonction de hachage robuste et l’avantage d’utiliser une fonction déjà existante. De plus, la problématique du stockage de mots de passe a des enjeux légaux en plus des enjeux liés à la sécurité.

Enfin, il est intéressant de noter que dans tous les cas les mots de passe azerty et matrix ont été trouvés rapidement, alors que le mot de passe yep59f$4txwrr n’a jamais été trouvé. En effet, comme celui-ci n’est présent sur aucune liste, le seul moyen de le trouver est d’effectuer un brute force exhaustif sur 13 caractères, ce qui est une opération très couteuse en temps (due au grand nombre de possibilités de mots de passe). Ceci montre aussi l’importance pour une application web de forcer une politique de mot de passe forte.