Dans un précédent article, nous avons vu pourquoi il était important de stocker les mots de passe en base de données avec des fonctions de hachage robustes telles que Bcrypt et Argon2. Cela permet notamment de rendre totalement ineffectif des attaques brute force ou des attaques par dictionnaire.
Cependant, une problématique est régulièrement relevée sur des applications déjà existantes : comment utiliser les dernières recommandations sur le stockage des mots de passe sur une base de données déjà existante ?
Quelles sont les recommandations sur le stockage sécurisé des mots de passe ?
Avant d’entrer dans le vif du sujet, quelques précisions sur les recommandations de l’OWASP sur le stockage des mots de passe :
- Utiliser Argon2id avec une configuration minimale de 19 MiB de mémoire, un nombre d’itérations de 2, et 1 degré de parallélisme.
- Si Argon2id n’est pas disponible, utiliser Scrypt avec un paramètre de coût CPU/mémoire minimal de (2^17), une taille de bloc minimale de 8 (1024 octets) et un paramètre de parallélisme de 1.
- Pour les anciens systèmes utilisant Bcrypt, utiliser un facteur de travail de 10 ou plus et avec une limite de mot de passe de 72 octets.
- Si la conformité FIPS-140 est requise, utiliser PBKDF2 avec un facteur de travail de 600 000 ou plus et une fonction de hachage interne de HMAC-SHA-256.
- Utiliser un poivre pour fournir une défense supplémentaire en profondeur (bien que seul, il n’apporte aucune caractéristique de sécurité supplémentaire).
Dans la suite de l’article, nous allons montrer comment mettre à jour les mots de passe avec la fonction de hachage Argon2id.
Pour ce faire, nous partirons de 2 cas concrets :
- Les mots de passe sont stockés en clair ou avec un algorithme de chiffrement.
- Les mots de passe sont stockés avec un hash inadapté (md5, sha1, sha2, sha3…).
Cependant nous n’allons pas implémenter un système de poivre. Dans le cas où vous souhaiteriez rajouter un poivre, il suffit de hasher l’ensemble poivre + mot de passe. Pour rappel, le poivre est identique pour tous les utilisateurs.
Par ailleurs, nous fournirons du code PHP comme exemple, mais les algorithmes peuvent se décliner sur d’autres langages.
Les mots de passe sont stockés en clair ou avec un algorithme de chiffrement
Modifier les mots de passe en base de données avec le hash Argon2id
En préambule, rappelons que le stockage en clair des mots de passe est interdit dans certains pays comme la France. Il est donc impératif de changer la gestion des mots de passe.
Et si vous partez de zéro (cas que nous ne verrons pas dans cet article), vous pouvez consulter le contenu suivant qui détaille : Comment stocker les mots de passe de manière sécurisée dans une base de données ?
Revenons maintenant à notre sujet.
Dans le cas ici présenté, vu que les mots de passe sont facilement identifiables (car stockés en clair), il suffit de mettre à jour les mots de passe avec l’algorithme suivant :
Ce qui aura pour effet de transformer la table utilisateurs de :
ID | Name | Password |
---|---|---|
1 | admin | azerty |
2 | toto | matrix |
3 | billy | yep59f$4txwrr |
4 | tata | matrix |
5 | titi | freepass |
6 | attaquant | tesPwndPassword |
…en :
ID | Name | Password |
---|---|---|
1 | admin | $argon2id$v=19$m=65536,t=4,p=1$UjYzcS42QmhmLjFsa3lrYQ$7t8mfl Th2JhcVqSTdQ0GwtLtMh6plWECubPEH8NjEUM |
2 | toto | $argon2id$v=19$m=65536,t=4,p=1$SURlcDNVUDZFWEVlQy5UYg$mv m0Iohc9nd/KOLP5kAw6/WB+PAK0Nt6QGPTsdQa8aw |
3 | billy | $argon2id$v=19$m=65536,t=4,p=1$WTlMMDJDeUVMeDNONXRDaQ$u ccT7LUNmJcj8+ZLqtT+U7m+IJePgCuvv8BxCzRYKG4 |
4 | tata | $argon2id$v=19$m=65536,t=4,p=1$c00udFlHMm1LMXhwcDBjVg$NR0I QiLqC0dMSgYHbtEhcAGdrFqhdI4YoOTjtMW1zZA |
5 | titi | $argon2id$v=19$m=65536,t=4,p=1$U1Y2dm44cXlMWHp0SkFhVg$lYj9k Lfx6zUNOTDUXHDhpEFyBdSJXDv9qjxA0+4oEYw |
6 | attaquant | $argon2id$v=19$m=65536,t=4,p=1$NmFqZVhOaTRsN2t4L3lWRw$UJbZ AJcm7ujhLv/Y/DcyMDqD42ME95l+Cd5Kh2XJ2G0 |
Notons au passage que l’application n’impose pas une bonne politique de mot de passe, ce qui est essentiel pour éviter des risques de compromission. Il est donc fortement recommandé d’imposer l’utilisation de mots de passe forts à la création.
Pour plus d’informations, nous vous renvoyons vers notre article : Comment sécuriser les systèmes d’authentification, de gestion de sessions et de contrôle d’accès de vos applications web ?
Grâce à la fonction password_get_info
, uniquement les mots de passe qui ne sont pas de l’Argon2 seront transformés. Cela permet d’exécuter le script plusieurs fois sans risquer de hasher plusieurs fois les mots de passe.
Par ailleurs, il est important de faire une sauvegarde de la base de données pour ne pas risquer de corrompre les utilisateurs en cas de soucis de migration.
NB : si un algorithme de chiffrement est utilisé, il faut directement hasher les mots de passe en clair, et non la donnée chiffrée.
Vérifier la validité des mots de passe utilisateurs
Pour confirmer la présence ou non d’un utilisateur, il suffit de comparer avec la fonction password_verify
(ou équivalent dans d’autres langages) le mot de passe et le hash.
Même si l’opération est ici invisible pour l’utilisateur, il est tout de même fortement recommandé d’inciter les utilisateurs à changer leur mot de passe après cette modification.
Le fonctionnement sera le suivant :
On rajoute une colonne sur la table utilisateur, par exemple isUpdatePassword
qui vaut false
par défaut :
sqlite> ALTER TABLE USERS ADD isUpdatePassword bool default false;
sqlite> select * from USERS;
id|name|password|isUpdatePassword
1|admin|$argon2id$v=19$m=65536,t=4,p=1$UjYzcS42QmhmLjFsa3lrYQ$7t8mflTh2JhcVqSTdQ0GwtLtMh6plWECubPEH8NjEUM|0
2|toto|$argon2id$v=19$m=65536,t=4,p=1$SURlcDNVUDZFWEVlQy5UYg$mvm0Iohc9nd/KOLP5kAw6/WB+PAK0Nt6QGPTsdQa8aw|0
3|billy|$argon2id$v=19$m=65536,t=4,p=1$WTlMMDJDeUVMeDNONXRDaQ$uccT7LUNmJcj8+ZLqtT+U7m+IJePgCuvv8BxCzRYKG4|0
4|tata|$argon2id$v=19$m=65536,t=4,p=1$c00udFlHMm1LMXhwcDBjVg$NR0IQiLqC0dMSgYHbtEhcAGdrFqhdI4YoOTjtMW1zZA|0
5|titi|$argon2id$v=19$m=65536,t=4,p=1$U1Y2dm44cXlMWHp0SkFhVg$lYj9kLfx6zUNOTDUXHDhpEFyBdSJXDv9qjxA0+4oEYw|0
6|attaquant|$argon2id$v=19$m=65536,t=4,p=1$NmFqZVhOaTRsN2t4L3lWRw$UJbZAJcm7ujhLv/Y/DcyMDqD42ME95l+Cd5Kh2XJ2G0|0
Ensuite dans le formulaire de connexion, l’utilisateur sera redirigé vers un formulaire de réinitialisation de mot de passe si son mot de passe n’a pas été changé, sinon il peut naviguer de manière classique.
Ici, nous sauvegardons dans la session l’identifiant ou le nom de l’utilisateur après avoir validé la connexion.
Enfin, le code pour traiter le formulaire de changement de mot de passe sera le suivant :
Note : Ici nous vérifions uniquement que l’ancien mot de passe est différent du nouveau. Dans l’idéal, il faudrait aussi vérifier que le mot de passe respecte une bonne politique.
Les mots de passe sont stockés avec un hash inadapté (md5, sha1, sha2, sha3…)
Migrer les hash vers Argon2id
Dans ce cas de figure, la migration des données est plus complexe, car les mots de passe ne sont pas identifiables. Ici, ce que nous conseillons est de faire de l’Argon2id directement sur le hash stocké en base de données.
Pour notre exemple, nous nous plaçons dans un cas ou le hash md5 est utilisé, avec du sel et du poivre pour être dans le cas le plus complexe possible.
Les mots de passe sont donc stockés en base de cette manière : md5 (sel + mot de passe + poivre).
La connexion aura donc cette forme :
Le code de conversion sera le même que l’exemple précédent. En effet c’est directement le hash md5 qu’on veut encoder, valeur qui est déjà présente dans la base de données (colonne password).
Le formulaire de connexion va cependant être plus complexe, car nous avons plusieurs conditions à valider :
- À la première connexion valide de l’utilisateur, lui demander un nouveau mot de passe et stocker uniquement l’argon2id de ce mot de passe
- Devoir gérer 2 cas dans le formulaire de connexion :
- Si la colonne
isUpdatePassword
vautfalse
, alors l’utilisateur n’a pas changé son mot de passe et le hash sera : argon2id [md5 (sel + mot de passe + poivre)]. - Si la colonne
isUpdatePassword
vautfalse
alors, l’utilisateur a changé son mot de passe et le hash sera : argon2id (mot de passe + poivre).
- Si la colonne
Et le formulaire de reset sera le suivant :
Si vous utilisez des hash adaptés
Les algorithmes considérés comme fiables pour stocker les mots de passe ont un point positif : ils peuvent s’adapter avec le temps et l’évolution du matériel en augmentant leur cout. Il peut donc être pertinent d’anticiper l’évolution des algorithmes dans votre formulaire de login.
En PHP vous pouvez utiliser la fonction password_needs_rehash.
Auteur : Thomas DELFINO – Pentester @Vaadata