Hachage sécurisé des mots de passe en Go
5 décembre 2023
0 minutes de lectureLes identifiants utilisateur sont les informations nécessaires pour authentifier l’identité d’un utilisateur et lui accorder l’accès à un système ou à une application. Il s’agit généralement d’un nom d’utilisateur ou d’une adresse e-mail, ainsi que d’un mot de passe. Un nom d’utilisateur peut être stocké en texte brut dans une base de données, mais les informations sensibles comme les adresses e-mail ou les mots de passe ne devraient pas l’être.
Si un acteur malveillant accède à la base de données dans laquelle vous stockez ces informations, vous ne voulez pas les lui livrer facilement. Vous devez plutôt vous assurer qu’il ne puisse pas y accéder, même en mobilisant toutes ses ressources et en faisant preuve de détermination. C’est là que le hachage entre en jeu.
Dans la continuité de notre précédent article sur le hachage des mots de passe en Java, nous abordons ici le hachage des mots de passe en Go. Commençons par revoir certains concepts et bonnes pratiques du hachage des mots de passe.
Utilisez le hachage pour les mots de passe
Une fonction de hachage est une fonction mathématique qui prend une entrée (ici, un mot de passe) et produit une chaîne de caractères de taille fixe, généralement composée de lettres et de chiffres. La sortie, appelée valeur de hachage ou code de hachage, est propre à l’entrée. Les fonctions de hachage sont conçues pour être unidirectionnelles : il est donc pratiquement impossible, d’un point de vue computationnel, de retrouver l’entrée d’origine à partir de la valeur de hachage. Pour sécuriser les mots de passe, ceux-ci sont hachés et stockés sous forme de valeurs de hachage plutôt qu’en texte brut.
La différence entre le hachage et le chiffrement, c’est que le hachage est une fonction unidirectionnelle, tandis que le chiffrement est bidirectionnel et permet de retrouver la valeur d’origine à partir de la valeur chiffrée. Le chiffrement est couramment utilisé pour les données sensibles comme les adresses e-mail et les autres informations personnelles identifiables (IPI), mais pour les mots de passe, le hachage est la méthode recommandée.
Mais si le hachage est une fonction unidirectionnelle, comment vérifier les identifiants utilisateur ? Le processus est simple : nous créons le hachage du mot de passe en texte brut fourni par l’utilisateur, puis nous le comparons au hachage stocké dans notre base de données. Si les hachages correspondent, le mot de passe est correct. Dans le cas contraire, les identifiants sont incorrects. L’avantage du hachage, c’est que nous n’avons jamais besoin de connaître le mot de passe réel de l’utilisateur !
Techniques d’attaque
Nous avons dit plus tôt qu’il faut s’assurer qu’un acteur malveillant ne puisse pas accéder à ces informations, même en mobilisant toutes ses ressources et en faisant preuve de détermination. Il existe des méthodes qui lui permettent de retrouver le mot de passe à partir du hachage.
Un pirate peut avoir accès à une liste de vos mots de passe provenant d’autres sources, comme des comptes piratés. Il peut utiliser une rainbow table pour tenter de casser le hachage. Si les hachages correspondent, le mot de passe est compromis. Des attaques par force brute peuvent également être menées : différentes combinaisons de mots de passe sont alors testées pour vérifier si le hachage du mot de passe généré correspond à celui du mot de passe d’origine.
Même si ces méthodes nécessitent des ressources à haut débit ou une grande puissance de calcul, elles ont fait leurs preuves contre d’anciens algorithmes de hachage, comme ceux de la famille SHA1, compte tenu de la puissance de calcul disponible aujourd’hui .
Il est donc extrêmement important d’utiliser des algorithmes de hachage plus récents et d’autres bonnes pratiques pour que le hachage généré soit impossible à casser, ou qu’il faille plusieurs décennies de permutations et de combinaisons pour y parvenir.
Bonnes pratiques de hachage
Voici quelques bonnes pratiques pour utiliser des méthodes de hachage modernes.
Salage
Un sel est une valeur générée aléatoirement (généralement des données binaires aléatoires, comme des octets) qui est ajoutée au mot de passe lors de la création du hachage. Chaque mot de passe possède son propre sel. Cela ajoute une couche de sécurité, car l’acteur malveillant doit casser le sel en plus du hachage. Le cassage du hachage se déroule donc en deux étapes et demande davantage de ressources.
La longueur du sel dépend de l’algorithme de hachage utilisé. Avec les algorithmes récents, le sel est généré automatiquement : il n’est donc pas nécessaire d’en fournir un. Pour en savoir plus, consultez cette fiche de prévention OWASP sur le stockage des mots de passe.
Algorithmes de hachage
Les algorithmes de hachage jouent un rôle essentiel dans le hachage des mots de passe. Il est indispensable d’en choisir un qui résiste au cassage. Les algorithmes modernes permettent de personnaliser des paramètres comme le facteur de coût et le nombre d’itérations, ce qui rend le hachage plus difficile à casser. Compte tenu de la puissance de calcul disponible, il est conseillé de choisir des valeurs élevées. En règle générale, plus le facteur de coût ou le nombre d’itérations est élevé, plus le hachage est difficile à casser.
Par bonne pratique, choisissez la valeur maximale du facteur de coût ou du nombre d’itérations que votre système peut gérer sans incidence importante sur les performances.
Points à prendre en compte pour les algorithmes de hachage
Voici quelques éléments à prendre en compte lors de l’implémentation d’un algorithme de hachage.
Gestion des collisions de hachage
Une collision de hachage se produit lorsque deux entrées différentes produisent la même valeur de hachage. Les algorithmes de hachage modernes sont conçus pour réduire au minimum le risque de collision, mais celui-ci existe toujours. Pour l’atténuer, il est recommandé d’utiliser des valeurs de hachage plus longues, comme SHA-256 ou SHA-512, qui offrent un espace de sortie plus vaste et réduisent la probabilité de collisions.
Nombre d’itérations
Le nombre d’itérations est un facteur important pour le hachage des mots de passe. Plus ce nombre est élevé, plus le cassage du hachage demande de ressources et plus l’attaque est ralentie. Toutefois, il est important de trouver un équilibre entre sécurité et performances, car un nombre trop élevé d’itérations peut ralentir le processus d’authentification pour les utilisateurs légitimes. Il est recommandé de choisir une valeur qui assure une sécurité suffisante sans nuire de manière importante aux performances.
Mettez régulièrement à jour les algorithmes de hachage
Avec les progrès technologiques et l’augmentation de la puissance de calcul, de nouveaux algorithmes de hachage sont mis au point, tandis que les anciens sont abandonnés. En effet, ces derniers ne suivent plus le rythme des avancées et deviennent des cibles faciles pour les attaquants.
Hachage des mots de passe en Go
Le package crypto de Go fournit des fonctions de hachage intégrées pour des algorithmes populaires tels que argon2, scrypt, bcrypt, pbkdf2, et bien d’autres. Ces fonctions sont particulièrement utiles pour implémenter le hachage des mots de passe. Le package bcrypt fournit même une fonction CompareHashAndPassword permettant de comparer les mots de passe en texte brut aux mots de passe hachés et de vérifier s’ils correspondent.
Argon2
Argon2 est actuellement considéré comme l’algorithme de hachage le plus sûr (même si cela pourrait changer à l’avenir). Il existe en trois variantes : Argon2d, qui maximise la résistance aux attaques de cassage par GPU ; Argon2i, optimisé pour résister aux attaques par canal auxiliaire ; et Argon2id, qui combine les deux.
La fiche de prévention OWASP sur le stockage des mots de passe recommande l’algorithme hybride Argon2id pour stocker les mots de passe. Elle préconise une taille minimale de mémoire (m), un nombre minimal d’itérations (t) et un degré de parallélisme (p) comme suit :
m=47104 (46 MiB), t=1, p=1 (Ne pas utiliser avec Argon2i)
m=19456 (19 MiB), t=2, p=1 (Ne pas utiliser avec Argon2i)
m=12288 (12 MiB), t=3, p=1
m=9216 (9 MiB), t=4, p=1
m=7168 (7 MiB), t=5, p=1
Implémentation en Go
Examinons maintenant un exemple d’implémentation d’Argon2id pour générer un hachage à partir d’un mot de passe en texte brut et le comparer au hachage.
Configuration
Pour commencer, nous allons créer une structure qui stockera tous les paramètres de configuration nécessaires au hachage avec Argon2id :
La structure Argon2idHash contient les paramètres suivants : time, memory (coût de calcul), threads (parallélisme), keyLen et saltLen. Ces paramètres permettent de générer un hachage à partir d’un mot de passe en texte brut.
Nous allons ensuite écrire une fonction constructeur pour initialiser la structure avec les valeurs fournies.
Génération du sel
Pour faciliter la génération de valeurs aléatoires, notamment celle du sel, nous avons créé la fonction randomSecret. Elle prend la longueur souhaitée pour la valeur secrète aléatoire et utilise la fonction rand.Read pour la générer.
Génération du hachage
Nous créons ensuite une méthode de hachage, GenerateHash, pour la structure Argon2idHash. Cette méthode permet de générer un hachage à partir des valeurs configurées. Si aucun sel aléatoire n’est fourni, nous en générons un de la longueur définie dans la configuration. Nous utilisons ensuite toutes les valeurs configurées pour générer le hachage avec la fonction argon2.IDKey. Si la génération réussit, nous renvoyons la paire hachage-sel.
Comparaison des mots de passe
Pour comparer les mots de passe, nous créons une autre méthode, Compare, pour la structure Argon2idHash. Cette méthode prend le hachage et le sel enregistrés, ainsi que le mot de passe à comparer.
Nous générons d’abord un hachage à l’aide de la fonction GenerateHash créée à l’étape précédente. Cette fonction prend en entrée le mot de passe et le sel fournis.
Nous comparons ensuite le hachage généré au hachage stocké à l’aide de la fonction bytes.Equal. Si les deux hachages sont identiques, ils correspondent. Sinon, le hachage stocké ne correspond pas au mot de passe fourni.
Dans cet exemple, nous avons utilisé la fonction argon2.IDKey function pour générer le hachage. Comme indiqué précédemment, d’autres packages crypto fournis par Go proposent des fonctions similaires permettant de générer des hachages de mots de passe sécurisés, comme celui de notre exemple.
Scrypt
Scrypt arrive en deuxième position parmi les algorithmes de hachage. Argon2id doit être votre premier choix ; s’il n’est pas disponible, scrypt constitue une solution de repli.
Comme pour Argon2id, la fiche de prévention OWASP sur le stockage des mots de passe recommande les valeurs suivantes pour les champs : le paramètre de coût minimal en CPU/mémoire (N), la taille de bloc (r) et le degré de parallélisme (p).
N = 2^17 (128 MiB), r = 8 (1024 bytes), p = 1
N = 2^16 (64 MiB), r = 8 (1024 bytes), p = 2
N = 2^15 (32 MiB), r = 8 (1024 bytes), p = 3
N = 2^14 (16 MiB), r = 8 (1024 bytes), p = 5
N = 2^13 (8 MiB), r = 8 (1024 bytes), p = 10
La documentation du package Go scrypt fournit un exemple de génération d’une clé de hachage avec scrypt. Les opérations de génération et de comparaison devraient être très similaires à leur implémentation pour la structure Argon2idHash.
Bcrypt
bcrypt doit être votre dernier recours, tout comme PBKDF2 (que nous verrons ensuite) si vous devez respecter la norme FIPS 140. Selon la fiche de prévention OWASP sur le stockage des mots de passe, le facteur de coût doit être aussi élevé que les performances du serveur de vérification le permettent, avec une valeur minimale de 10.
Le package Go bcrypt inclut des fonctions de génération et de comparaison de hachages. Nous ne présenterons donc pas d’exemple ici. Consultez la documentation du package Go pour découvrir des exemples.
PBKDF2 (conforme à FIPS 140)
Selon la fiche de prévention OWASP sur le stockage des mots de passe, le facteur de coût de PBKDF2 est défini par un nombre d’itérations, à ajuster en fonction de l’algorithme de hachage interne utilisé.
PBKDF2-HMAC-SHA1 : 1 300 000 itérations
PBKDF2-HMAC-SHA256 : 600 000 itérations
PBKDF2-HMAC-SHA512 : 210 000 itérations
Exemple de hachage
Comme PBKDF2 est le seul algorithme de notre liste conforme à FIPS 140, nous allons voir comment l’utiliser pour hacher des mots de passe en Go. Commençons.
Voici la fonction Key du package pbkdf2, que nous utiliserons pour générer un hachage à partir d’un mot de passe en texte brut :
Pour effectuer le hachage, vous aurez besoin du mot de passe et du sel sous forme de tranches d’octets, ainsi que du nombre d’itérations et de la longueur souhaitée de la clé ou du hachage généré. Une fonction de hachage est également nécessaire. Dans notre cas, le mot de passe désigne le mot de passe en clair que nous voulons hacher, tandis que le sel est une valeur générée aléatoirement. Vous pouvez configurer le nombre d’itérations et la longueur de la clé. Pour la fonction de hachage, nous utiliserons sha3.New256.
Configuration
Génération du hachage
Comparaison des mots de passe
Cette implémentation est très similaire à celle de Argon2idHash. Vous pouvez donc modifier l’exemple de code complet pour utiliser PBKDF2Hash à la place.
Code complet
Rassemblons maintenant tous les éléments.
Structure du projet
Le fichier argon2id.go :
Le fichier main.go :
Choisissez votre algorithme avec soin
Les exemples ci-dessus vous donnent un excellent aperçu de l’implémentation d’algorithmes de hachage sécurisés pour le stockage des mots de passe en Go. Gardez à l’esprit que tous les algorithmes de hachage ne se valent pas : choisissez le vôtre avec soin, en fonction de vos besoins et du niveau de sécurité actuel de l’algorithme. Ce qui est considéré comme un algorithme robuste aujourd’hui évoluera avec le temps. Il est donc judicieux de réévaluer régulièrement votre choix.
Sachez également que les outils SAST comme Snyk Code peuvent vous aider à repérer les algorithmes obsolètes et vous conseiller sur la marche à suivre pour les mettre à jour, si nécessaire. Choisissez votre algorithme de hachage des mots de passe avec soin, restez informé et bon codage !
