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>.
Login | Mot de passe |
admin | azerty |
toto | matrix |
billy | yep59f$4txwrr |
tata | matrix |
titi | freepass |
attaquant | tesPwndPassword |
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)
Login | Mot de passe (md5) |
admin | ab4f63f9ac65152575886860dde480a1 |
toto | 21b72c0b7adc5c7b4a50ffcb90d92dd6 |
billy | 47ad898a379c3dad10b4812eba843601 |
tata | 21b72c0b7adc5c7b4a50ffcb90d92dd6 |
titi | 5b9a8069d33fe9812dc8310ebff0a315 |
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.
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.
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 :
Login | Mot de passe (sha512) |
admin | df6b9fb15cfdbb7527be5a8a6e39f39e572c8ddb943fbc79a943438e9d3d85ebfc2ccf9e0eccd9346026c0b6876e0e01556fe56f135582c05fbdbb505d46755a |
toto | 11a25e88658143a853d280bf77f81ff391347aaba2db54a3aab0149b265276de419880762a473fc496388bcf70566d7cfd0346c34add40652f8f7b669caf9ec0 |
billy | fe9cb9b07725fd1cc3906232405119fedf9a020436630d3c1e0f632f73909e6ed9e731c149ac22743bbe9541881f35ceebf1d2782d469eb3b42968469d55a7a4 |
tata | 11a25e88658143a853d280bf77f81ff391347aaba2db54a3aab0149b265276de419880762a473fc496388bcf70566d7cfd0346c34add40652f8f7b669caf9ec0 |
titi | f767036acd951f5ddaf4eed5291c677db060055806dbcae69ca35d95847559dc8abce5011fd2b50833e760eca2d84d6daf1f078200f42b4fc10b58bad3761c88 |
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 :
- 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.
- Les rainbow tables ne fonctionnent pas avec des hash salés
- 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
Utilisateur | Sel | Hash sha512 avec sel |
admin | BGdd6d6^ZgvkMhKf@W3RqT | 7509d123bce1aa92331861cf8fd738a58205045123f0e25f0862477cb19d3ee0757cd99865c30b123ad1e7f1be1e31a6058090458cb9941031f5c36683c8446e |
toto | HZBD^@gL*wvoExo6yJ7hVB | 6b28830776de6ad7ef1dd8c221e0d53fec4532c623075d0216d937ab82ab284a56a461ce5d4ec77d1783665a262a6a1eb98627b1f6260da55dbb782d7cb75bc4 |
billy | wvVndjwcZJy!dwT4fBD@U^ | 2847b2605f6a1cd88399e6c9784c0e583799be9485cb128fe5f541f43636559067ec32de33e9b3fa2c15b15eec294cf262fd7aab2395dd64d6dbd9640b4fe6fd |
tata | QeNWm9NXqJ8m@m2^F7Kh9* | 165bc06b69fa2bfcd893bfde86358394406c87c7f7abba891cd10ed9fac887c54d52ed14310ad675078033e9bca80084d345fb2836933e55c60f734982430e2b |
titi | iQUemgw9M6Gw*&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
- Algorithme : Celui-ci peut prendre plusieurs versions en fonction de la version de bcrypt ($2$, $2a$, $2x$, $2y$ et $2b$)
- 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)
- Le sel : Au lieu de stocker le sel dans une colonne dédiée, celui-ci est directement stocké dans le hash final
- 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.
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.
Auteur : Thomas DELFINO – Pentester @Vaadata