Les mots de passe et leurs stockages représentent une vraie problématique aujourd’hui, bien qu’il existe de nombreuses solutions côté client (pour la création/stockage de mots de passe) jugées sûres (tels que les gestionnaires de mots de passe à condition d’avoir une master key robuste), la gestion côté serveur n’est pas toujours bien assurée, cet article à donc pour objectif de définir les grandes lignes d’une gestion sécurisée des mots de passe, notamment dans les backend (LDAP, base de données, etc.).

Pour rappel, chaque définition des termes employés dans cet article peut-être retrouvée et expliquée sur le site chiffrer.info, néanmoins, le terme crypter, n’existe pas !

Comment stocker ses mots de passe en base :

  • En clair : Stocker les mots de passe sans aucun traitement avant enregistrement dans la base, cette idée est à proscrire, car elle ne procure aucune sécurité, un attaquant qui aurait accès à la base de données aurait accès à l’ensemble des mots de passe …
  • De manière chiffré (donc réversible) : Cela peut sembler être une bonne pratique, mais même si vous utilisez un chiffrement robuste tel que AES256, cela n’est pas une bonne idée. Le principal problème de cette méthode et qu’il est nécessaire d’avoir la clé pour déchiffrer les précieux sésames, si elle est perdue vous ne pourrez plus déchiffrer les secrets et si elle est volée, l’attaquant n’aura pas de mal à déchiffrer les mots de passe. Il y a donc de fortes chances pour que la clé secrète soit stockée sur le serveur hébergeant la base de données.
  • En les hashant : Hasher les mots de passe représente la meilleure méthode puisque non-réversible, cette méthode permet de créer une empreinte de la chaîne fournie (le mot de passe) et de stocker cette empreinte en base, cette méthode est donc la plus recommandée à condition d’utiliser un algorithme de hashage robuste !

Fonctions de hashage : Comparaison des algorithmes

Cet article traite de l’algorithme de hashage qui est certainement le plus connu bien que jugé obsolète et insécurisé aujourd’hui, à savoir MD5, ainsi que ces solides concurrents bCrypt, sCrypt et Argon2 que SYNETIS préconise à l’heure actuelle.

Il existe tout un tas d’autres algorithmes de hachage, donc certains qui sont également jugés robustes tel que PBKDF2, cependant il n’est  pas possible de parler de tous les algorithmes, cet article se concentre sur ceux qui sont le plus souvent supportés nativement (tel que bCrypt et Argon2 qui sont par exemple supporté nativement dans PHP, qui reste le langage du web le plus utilisé sur la planète).

  • MD5 : Certainement l’algorithme de hashage le plus connu ! Inventée en 1991, une faille critique est découverte en 1996 (Il est possible de créer des collisions = une collision désigne une situation dans laquelle deux données ont un résultat identique avec la même fonction de hachage). MD5 est jugé complètement obsolète depuis 2004, cela fait donc 15 ans que l’on utilise couramment un algorithme obsolète, sans notion de “salt” ni de “bouclage”. Le hash MD5 du mot “Synetis” sera le suivant :

    bfb970843b550b7ea910814dec14ffcd

  • bcrypt : Créer en 1999, c’est un algorithme jugé robuste encore aujourd’hui, il se veut résilient aux attaques par CPU & GPU en intégrant un sel ainsi qu’un coût (nombre d’itérations pour créer le hash) nativement.Un hash bCrypt ($2y$) du mot “Synetis” avec 10 tours de boucle ($10$) pourrait être le suivant :

    $2y$10$H06aNwzet21CaZo96U8utOi9qq8.ag0nSp53q7QFdbTnZOowbEIZu

    Si on génère un nouveau hash, celui-ci ne sera pas le même, car il inclut également un sel aléatoire à chaque génération !

  • sCrypt : Créer en 2009, c’est un algorithme qui est également jugé encore robuste aujourd’hui, tout comme bCrypt il se veut également résilient aux attaques par CPU & GPU, il se veux être un meilleur choix que bCrypt aujourd’hui, il intègre de nouveaux paramètres, le coût en mémoire et le coût CPU, ces deux paramètres permettent de toujours mieux se protéger contre la parallélisation et les attaques par bruteforce.
    Si on génère un hash sCrypt du mot “Synetis” d’une longueur 32 avec une difficulté CPU de 2014, une difficulté mémoire de 8 et une difficulté de parallélisation de 1 on obtiendrai par exemple ce hash :

    5ca4fe8fab9fb3d7331ec7ae60b30e109eca0ba53f50be6a62b77cdcb266ba9d

  • Argon2 : Argon2 est “le petit nouveau” qui a remporté la Password Hashing Compétition en 2015, il se dérive en 3 fonctions :
    • Argon2I : Optimisé contre les attaques par canal auxiliaire (liée à des attaques sur le CPU), par exemple les failles Spectre & Meltdown
    • Argon2D : Optimisé contre les attaques par GPU
    • Argon2ID : Pour se prémunir des attaques par canal auxiliaire & GPU

Argon2 permet une protection supplémentaire à sCrypt avec Argon2I

Sans rentrer dans les détails, voici un exemple de nombre de hash qu’un GPU RTX2080Ti est capable de générer par secondes pour ces algorithmes (Hors Argon2 qui n’est pas encore supporté par hashcat mais dont les résultats doivent être proche de sCrypt).

Algorithme hash/s
MD5 551.2 GH/s
MD5 (+sel) 229.9 GH/s
NTLM 3195.7 MH/s
bCrypt 88200 H/s
sCrypt 554.1 kH/s

Le schéma suivant montre la possibilité en termes de cassage de mot de passe (sans optimisations) avec 448 GPU RTX2080 (difficilement reproductible du au matériel nécessaire) , bien que sCrypt et Argon2 ne font pas partie de ce schéma, on voit bien que bCrypt est celui qui pose le plus de résistance.

Terahash-cluster-of-448x
Terahash-cluster-of-448 RTX 2080 password cracking : https://terahash.com/

Sel et poivre :

Pour remédier aux principaux problèmes des fonctions de hachage dites “simple” (MD5, SHA1, SHA256, SHA512), des “rustines” ont été inventées, notamment :

  • La notion de “sel” (salt) qui se veut être une chaîne générée aléatoirement et concaténée au mot de passe (en préfix, en suffixe, ou les deux, etc.). Le sel est généralement stocké à côté du hash (en base de données par exemple), pour les algorithmes simplistes. Il revient donc au développeur implémentant le MD5 par exemple, de coder lui-même la gestion, stockage, génération du sel. Le désavantage est que si la base de données est compromise (SQL Injection), tous les hashs et les salts sont à la mercie de l’attaquant.
  • La notion de “poivre” est une chaîne globale à tous les utilisateurs (contrairement au sel), et qui est stockée dans le code source applicatif (et non pas en base).

L’utilisateur de ces deux méthodes, en plus d’accroitre le temps de calcul nécessaire au bruteforce, permet de rendre inutile les dictionnaires de mots de passe & rainbow tables (anti-analyse fréquentielle).

Pour faire court, ces dictionnaires contiennent un grand nombre de hash, ainsi si on génère le hash md5 du mot de passe “azerty” il sera rapidement retrouvé dans un de ces dictionnaires, car c’est un mot de passe très utilisé, mais si on ajoute un sel aléatoire et robuste à ce mot de passe tel que Bb1^v7*o, cela améliorera l’entropie du mot de passe et le hash généré ne sera pas présent dans un dictionnaire de mot de passe.

Pour MD5 il existe de nombreuses utilisations avec le sel, par exemple il est possible de faire md5(md5($pass).md5($salt)) ou md5($salt.$pass.$salt).

Problèmes et solutions de ces algorithmes

L’utilisation des divers paramètres de bCrypt, sCrypt et Argon2 entraine un temps plus important de calcul du hash que MD5, plus ces paramètres seront élevés, plus vos hash seront robustes, mais cela entrainera également une plus forte consommation de ressources / temps pour créer le hash, ce qui peut généralement créer deux principaux problèmes :

  • Deny de service : Dans le cas de paramètres forts, un attaquant pourrait utiliser la fonctionnalité d’inscription ou de connexion et entrer des mots de passe très très longs afin de faire consommer une grande quantité de ressources aux serveurs, en multipliant les requêtes, il serait en capacité de faire crasher le serveur !
    • Pour corriger ce problème, plusieurs solutions existent :
      • Limiter la taille du mot de passe, 100 caractères maximum semblent être une bonne limite
      • Utiliser un système de captcha sur les pages où il est important de limiter le bruteforce tel que la page de connexion/inscription afin de limiter les multiples requêtes.
      • Imposer un rate-limit (type tarpit) sur ces endpoints potentiellement consommateurs ;
  • Time based user enumération : Dans le cas d’une mauvaise implémentation (vérification de l’existence de l’utilisateur, puis si celui-ci existe calcule du hash, sinon message d’erreur) il est possible pour un attaquant de vérifier qu’un utilisateur existe, en cas de réponse courte, l’utilisateur n’existe pas, en cas de réponse longue, le hash sera calculé donc le temps de réponse pourra être plus long
    • Pour remédier à ce problème, il est donc nécessaire de vérifier si l’utilisateur existe ET (et non pas PUIS) de calculer le hash du mot de passe fourni.
    • La CVE “CVE-2018-15473” concernant l’énumération de compte SSH se fonde notamment sur ce principe

Conclusion

Les algorithmes types MD5 ou SHA1 sont à bannir des nouveaux développement, car ils sont sujets à des collisions qui ont été démontrées par le passé ; et ce même si ue notion de salt, pepper et loop est ajouté à leur utilisation.

En termes de robustesse face à toutes les nouvelles attaques cryptographiques à l’encontre de ces algorithmes, le grand vainqueur actuel est Argon2. Seulement, cet algorithme est considéré comme “jeune” (2015). D’une manière générale, dans le monde de la cryptologie, l’ancienneté d’un algorithme couplé à sa robustesse prône avant tout. Ainsi BCrypt est un excellent candidat actuel datant de 1999. La jeunesse de Argon2 le rend encore inconnu du public, et donc peu implémenté nativement dans de multiples langages.


Références :