Comment bien stocker un mot de passe en base de données ?
1/ Pourquoi sécuriser les mots de passe ?
Pour protéger l’accès aux données de vos client/utilisateurs, il est important de bien sécuriser le stockage des mots de passe dans votre base de données afin d’éviter que les pirates :
- Ne récupèrent les mots de passe en cas de violation de la base de données
- Ne forcent l’accès grâce à des attaques brute force
2/ Dans la théorie : le stockage sécurisé d’un mot de passe
Le stockage sécurisé d'un mot de passe repose sur plusieurs éléments complémentaires :
-
La base est une fonction de hachage, qui transforme un mot de passe en des données binaires. Elle est déterministe (le même mot de passe donne toujours le même résultat), et surtout, irréversible (il n'est pas possible de retrouver le mot de passe original).
-
Cette fonction peut être itérative (où l'on va calculer le hash d'un hash d'un hash etc). Un nombre élevé d'itérations permet d'augmenter le temps de calcul et de fait le temps nécessaire à une personne malveillante pour trouver le mot de passe.
-
Dans la fonction de hachage, il est possible de rajouter du texte aléatoire au mot de passe. Ce texte sera différent pour chaque utilisateur, et permet d'augmenter artificiellement la complexité du mot de passe. On appelle cela le sel.
-
On peut également rajouter un autre texte, qui sera commun à tous les utilisateurs mais spécifique à l'application, toujours dans le but d'améliorer l'entropie du mot de passe. On appelle cela le poivre.
3/ Les recommandations sur le stockage de mots de passe
Plusieurs organismes éditent des recommandations sur le stockage des mots de passe.
L'ANSSI liste dans son guide Recommandations relatives à l'authentification multifacteur et aux mots de passe plusieurs préconisations dans le chapitre "4.6 Stockage des mots de passe".
Au niveau international, l'OWASP (Open Worldwide Application Security Project) publie une fiche mémo spécifique au stockage des mots de passe.
4/ Dans la pratique : le stockage de mots de passe avec WinDev
En WLangage, c'est la fonction HashChaîne()
qui permet de réaliser le hachage. Si les recommandations préconisent avant tout l'utilisation des algorithmes de hachages scrypt ou Argon2, celles-ci ne sont pas encore supportées par WinDev. On utilisera alors l'algorithme PBKDF2, qui fait aussi partie des recommandations et est disponible depuis la version 27.
Renvoyant un buffer contenant souvent des caractères non-imprimables, on privilégiera un encodage en Base64 pour en faire une chaîne stockable facilement en base de données.
L'algorithme PBKDF2 demande obligatoirement un sel. Pour le générer, on utilisera la fonction GénèreMotDePasse()
. Les recommandations de l'ANSSI conseillent un sel d'au moins 128 bits, soit 16 caractères minimum. Le poivre quant à lui est une valeur stockée dans une chaîne globale au projet, avec l'attribut <indétectable>
pour que la chaîne ne soit pas visible dans la mémoire.
Quant au nombre d'itérations demandées par la fonction HashChaîne, nous pourrons nous appuyer sur les recommandations de l'OWASP, qui préconisent au minimum 600 000 itérations pour l'algorithme PBKDF2-HMAC-SHA256.
Exemple de code avec WinDev pour implémenter ces recommandations :
// ================================
// Dans l'initialisation du projet
// ================================
gsPoivre est une chaîne <indétectable, immuable> = "gi1dy6!BDf5#CNsb3GF^hruKly3tj2m*"
// ================================
// Procédure de hachage du mot de passe
// ================================
PROCÉDURE HashMotDePasse(LOCAL sMotDePasse est une chaîne) : chaîne
sSel est une chaîne <indétectable> = GénèreMotDePasse(32)
nItérations est un entier = 600_000
sMotDePassePoivré est une chaîne = sMotDePasse + gsPoivre
sRésultat est une chaîne = HashChaîne(HA_PBKDF2_HMAC_SHA3_256,sMotDePassePoivré,sSel,nItérations).Encode(encodeBASE64SansRC)
Trace("Mot de passe :", sMotDePasse)
Trace("Poivre :", gsPoivre)
Trace("Sel :", sSel)
Trace("Résultat :", sRésultat)
RENVOYER sRésultat
// ================================
// Test de la fonction
// ================================
HashMotDePasse("123456")
// Mot de passe : 123456
// Poivre : gi1dy6!BDf5#CNsb3GF^hruKly3tj2m*
// Sel : SifirnEcaspafisteLokewindovmomYt
// Résultat : qbMLDp3pcnmeUyIR8aWtlA==
5/ En résumé : les bonnes pratiques à adopter pour sécuriser le mot de passe
Il n’existe pas de méthode infaillible pour sécuriser totalement les mots de passe mais les pratiques suivantes permettent de sécuriser efficacement les mots de passe en base de données :
- un Sel long et unique par utilisateur
- un Poivre, long, secret et spécifique à l'application
- un Algorithme de hachage lent
Au-delà du stockage même, la manipulation ou la transmission d'un mot de passe devront faire l'objet de précautions, par exemple :
- Sécuriser l’échange du mot de passe entre le frontend et le backend de contrôle (utilisation du HTTPS, pré-hachage côté frontend, etc)
-
Utiliser des mécanismes pour contrer les attaques brute force :
- Limitation par IP/seconde (avec augmentation exponentiel du temps d’attente)
- Limitation par nombre de tentative / seconde (avec blocage au bout de N tentatives infructueuses)
- Authentification 2 facteurs
- Blacklist d’IP
Besoin d’un accompagnement sur la sécurité de vos applications ? Contactez-nous dès maintenant pour un audit ou un accompagnement sur-mesure.