Skip to main content

Comment hacher correctement les mots de passe dans les applications Java !

Écrit par
Java engineering feature

24 mars 2022

0 minutes de lecture

Il existe plusieurs façons de stocker des mots de passe sensibles. Et même si avoir le choix est une bonne chose, en matière de stockage des mots de passe, faire le mauvais choix peut tourner au cauchemar côté sécurité. Voyons donc certaines de vos options en les passant au crible. Dans cet article, nous allons voir comment hacher les mots de passe dans vos applications Java. Ces principes s’appliquent à tous les écosystèmes, mais nous présenterons plus particulièrement la meilleure façon de gérer le hachage des mots de passe en Java.

La première question à vous poser lorsque vous décidez comment stocker des mots de passe sensibles est de savoir si vous souhaitez vraiment gérer vous-même l’authentification et l’autorisation. De nombreux services peuvent s’en charger à votre place. En utilisant un service d’authentification et d’autorisation, vous n’avez plus à vous soucier du stockage sécurisé des mots de passe dans votre application. De nombreux services commerciaux, comme Auth0 et Okta, simplifient cette tâche. Tant que le fournisseur que vous choisissez prend en charge des normes ouvertes comme OpenID Connect, vous pourrez facilement l’intégrer à vos applications Java (Spring Boot, par exemple). De plus, votre fournisseur peut également prendre en charge l’authentification multifacteur (MFA), afin de renforcer la sécurité de vos utilisateurs finaux.

Si vous devez stocker vous-même les mots de passe dans votre application Java, cet article est fait pour vous.

Hacher les mots de passe pour les stocker

Si vous stockez des noms d’utilisateur et des mots de passe dans votre application, quoi que vous fassiez, ne stockez pas les mots de passe en clair (oui, oui, ça semble évident !). Si votre système est compromis, un attaquant pourra lire directement ces mots de passe en clair et usurper l’identité de vos utilisateurs.

Pour éviter le redoutable mot de passe en clair, vous devez stocker une version hachée des mots de passe de vos utilisateurs. Pour cela, il vous faut un algorithme de hachage de mots de passe.

Qu’est-ce qu’un algorithme de hachage de mots de passe ?

Il existe différents types d’algorithmes de hachage, mais les algorithmes de hachage de mots de passe sont des fonctions à sens unique spécialement conçues pour traiter les mots de passe. Ils transforment un mot de passe en clair en une chaîne de données que vous pouvez stocker en toute sécurité dans une base de données et qui ne peut jamais être inversée (vous pouvez transformer un mot de passe en clair en empreinte, mais il est impossible de transformer une empreinte en mot de passe, d’où le terme « fonction à sens unique »).

Comment fonctionne le hachage des mots de passe ?

Les algorithmes de hachage de mots de passe sont utilisés lors de l’authentification des utilisateurs. Chaque fois qu’un utilisateur vous envoie son mot de passe (en clair), celui-ci est immédiatement haché. L’empreinte est ensuite soit stockée (lors de l’inscription), soit comparée à l’empreinte enregistrée de l’utilisateur pour vérifier que le mot de passe est correct (authentification).

Il existe de nombreux algorithmes de hachage, mais très peu conviennent aux mots de passe. Plusieurs types de fonctions de hachage ont des cas d’utilisation précis, comme la vérification de l’intégrité d’un fichier. Ces algorithmes de hachage ne conviennent pas aux mots de passe.

Ce qu’il vous faut, c’est un algorithme de hachage de mots de passe robuste. Un bon algorithme de hachage de mots de passe consomme une quantité importante de puissance de calcul et de mémoire. Pour les algorithmes de hachage de mots de passe, plus les ressources informatiques utilisées sont importantes, mieux c’est !

Les algorithmes de hachage de mots de passe robustes sont lents, ce qui rend les attaques par force brute moins efficaces. Nous pourrions approfondir le sujet, mais en résumé : pour les mots de passe, utilisez un algorithme de hachage robuste et gourmand en ressources afin d’assurer la meilleure protection possible à vos utilisateurs.

Quel est le meilleur algorithme de hachage de mots de passe ?

La première règle des algorithmes de hachage de mots de passe est la suivante : ne créez pas votre propre algorithme de hachage de mots de passe ! Sauf si vous êtes un cryptographe chevronné. Si ce n’est pas le cas, appuyez-vous sur les normes du secteur et choisissez des algorithmes et des bibliothèques éprouvés pour votre application.

Selon l’OWASP foundation, seuls quelques algorithmes sont à envisager pour le hachage moderne des mots de passe. Voyons les algorithmes recommandés !

Argon2id

À l’heure actuelle (en 2022), le meilleur algorithme à utiliser est Argon2id. Argon2 est une fonction de dérivation de clé qui a remporté le concours Password Hashing Competition. Il en existe trois variantes :

  • Argon2d : maximise la résistance aux attaques de cassage par GPU

  • Argon2i : optimisé pour résister aux attaques par canal auxiliaire

  • Argon2id : une variante hybride

Argon2id est recommandé pour les mots de passe, car il offre une protection équilibrée contre les attaques par canal auxiliaire et celles basées sur GPU. Vous pouvez configurer l’algorithme Argon2id en fournissant trois paramètres : la taille de la mémoire (m), le nombre d’itérations (t) et le degré de parallélisme (p).

À l’heure actuelle, il est conseillé d’utiliser Argon2id avec au moins 15 MiB de mémoire, 2 itérations et un degré de parallélisme de 1. L’algorithme inclut déjà un salage automatique pour chaque utilisateur, afin d’empêcher les attaques reposant sur des calculs préalables. Aucune configuration particulière n’est donc nécessaire.

Utiliser Spring Security Crypto avec Bouncy Castle

Le moyen le plus simple d’implémenter Argon2id dans votre application Java consiste à utiliser la bibliothèque spring-security-crypto. Bien qu’elle fasse partie du framework Spring, vous n’avez pas besoin d’utiliser le reste de Spring.

La bibliothèque spring-security-crypto propose un Argon2PasswordEncoder que vous pouvez utiliser. À mon avis, son nom n’est pas tout à fait approprié, car il s’agit techniquement d’un hacheur, et non d’un encodeur. La bibliothèque Spring utilise bouncycastle comme dépendance, qui fournit une implémentation complète de l’algorithme Argon2 en Java. On peut considérer la bibliothèque spring-security-crypto comme une interface intuitive par-dessus bouncycastle, qui en facilite l’utilisation. En plus de bouncycastle, vous aurez également besoin de la dépendance common-logging pour, vous l’aurez deviné, la journalisation.

<dependency>
   <groupId>org.springframework.security</groupId>
   <artifactId>spring-security-crypto</artifactId>
   <version>5.6.2</version>
</dependency>
<dependency>
   <groupId>commons-logging</groupId>
   <artifactId>commons-logging</artifactId>
   <version>1.2</version>
</dependency>
<dependency>
   <groupId>org.bouncycastle</groupId>
   <artifactId>bcpkix-jdk15on</artifactId>
   <version>1.70</version>
</dependency>

Par défaut, Argon2PasswordEncoder utilise la variante Argon2id avec m=4MiB, t=3 et p=1. Même si le nombre d’itérations est supérieur au minimum, vous devriez probablement envisager d’augmenter la consommation de mémoire. Dans l’exemple Java ci-dessous, j’utilise les paramètres minimaux : m=15Mib, t=2 et p=1. J’utilise également les valeurs par défaut pour la longueur du sel et de l’empreinte.

Argon2PasswordEncoder encoder = new Argon2PasswordEncoder(32,64,1,15*1024,2);
var myPassword = "ThisIsMyPassword";

var encodedPassword = encoder.encode(myPassword);
System.out.println(encodedPassword);

var validPassword = encoder.matches(myPassword, encodedPassword);
System.out.println(validPassword);

Le résultat de ce code ressemblera à ceci :

$argon2id$v=19$m=15360,t=2,p=1$YpRuuQhW1dHOimAnWD5TRU6Sebitu+fIrmIrenr+YOM$hkEXhhHpu2NUcPwhV4IUQelQdf4I8V+iyFsFiC8BYEisE3oWFv96zYeNA1i/awhaDo1XHz6Pp/1r55SS/I4AIA
True

Cette empreinte contient toutes les informations nécessaires, notamment les paramètres transmis à l’encodeur et les informations de salage. Vous pouvez ainsi vérifier facilement si le mot de passe d’origine correspond à l’aide de encoder.matches(). Cela fonctionne également avec une autre instance de Argon2PasswordEncoder configurée avec des paramètres différents.

Utiliser Argon2 Binding pour la JVM

La bibliothèque Argon2 Binding pour la JVM est une alternative à la bibliothèque Spring. Créée par Moritz Halbritter, elle utilise JNA pour appeler une bibliothèque C native. L’avantage de cette approche est qu’elle utilise l’implémentation originale des auteurs d’Argon2 et peut offrir de meilleures performances. Vous devez donc avoir accès à la bibliothèque C sous-jacente sur votre système.

Vous pouvez installer vous-même cette bibliothèque C à l’aide de votre gestionnaire de paquets, ou utiliser la bibliothèque qui contient un ensemble de bibliothèques Argon2 précompilées. La première option est recommandée, tout simplement parce qu’elle est plus légère.

Sans bibliothèques précompilées :

<dependency>
    <groupId>de.mkammerer</groupId>
    <artifactId>argon2-jvm-nolibs</artifactId>
    <version>2.11</version>
</dependency>

Avec des bibliothèques précompilées :

<dependency>
    <groupId>de.mkammerer</groupId>
    <artifactId>argon2-jvm</artifactId>
    <version>2.11</version>
</dependency>

Cette bibliothèque est assez simple à utiliser. Dans l’exemple de code ci-dessous, vous trouverez un équivalent de l’exemple Spring utilisant les mêmes paramètres minimaux.

Argon2 argon2 = Argon2Factory.create(Argon2Factory.Argon2Types.ARGON2id, 32, 64);
var myPassword = "ThisIsMyPassword";

var hash = argon2.hash(2,15*1024,1, myPassword.toCharArray());
System.out.println(hash);

var validPassword = argon2.verify(hash, myPassword.toCharArray());
System.out.println(validPassword);

scrypt

Si, pour une raison quelconque, vous ne pouvez pas utiliser Argon2id, scrypt constitue une bonne deuxième option. Comme Argon2id, scrypt est un algorithme robuste de hachage de mots de passe qui vous permet de configurer différents paramètres de coût afin d’augmenter les ressources qu’il consomme.

Selon l’OWASP Password cheat sheet, vous devez utiliser une valeur minimale pour le paramètre de coût CPU/mémoire (N), une taille de bloc minimale (r) et un paramètre de parallélisation (p). Les options ci-dessous correspondent aux valeurs minimales recommandées.

  • N=2^16 (64 MiB), r=8 (1024 bytes), p=1

  • N=2^15 (32 MiB), r=8 (1024 bytes), p=2

  • N=2^14 (16 MiB), r=8 (1024 bytes), p=4

  • N=2^13 (8 MiB), r=8 (1024 bytes), p=8

  • N=2^12 (4 MiB), r=8 (1024 bytes), p=15

Notez que les paramètres scrypt ci-dessus produisent tous des empreintes de mots de passe d’une robustesse équivalente. Ces options montrent simplement qu’en définissant différentes valeurs pour les paramètres N, r et p, vous pouvez faire utiliser davantage de ressources CPU ou mémoire à l’algorithme scrypt, selon vos préférences. La recommandation ci-dessous illustre une utilisation concrète que vous pouvez copier-coller pour plus de simplicité.

La bibliothèque spring-security-crypto prend en charge de nombreux algorithmes, dont SCrypt. Vous pouvez utiliser SCryptEncoder de la même façon que nous avons utilisé Argon2Encoder. Dans l’exemple ci-dessous, j’ai choisi la suggestion intermédiaire pour offrir un bon équilibre entre l’utilisation du CPU et de la mémoire.

SCryptPasswordEncoder encoder = new SCryptPasswordEncoder(16384, 8, 4, 32, 64);
var myPassword = "ThisIsMyPassword";

var encodedPassword = encoder.encode(myPassword);
System.out.println(encodedPassword);

var validPassword = encoder.matches(myPassword, encodedPassword);
System.out.println(validPassword);

Résultat :

e0804$8FQ4x/ntwEz2ZNu8QRyIQJlAXR+gQkiG3WulLMEq/kioVtaFiKE7sZDGgtmqUmwB8OE+f7Eagux9QXG478unLw==$RS1Bz5Uf30dWGxc+vtlkjj7tPnPdgq8YD1V8odhPW4A=
true

Utiliser BCrypt

Si Argon2id et scrypt ne sont pas disponibles, BCrypt constitue une autre option robuste. Si vous choisissez BCrypt, le facteur de travail doit être fixé à au moins 10.

Vous pouvez utiliser la bibliothèque spring-security-crypto comme précédemment. La charge de travail est définie par défaut sur 10, mais nous la réglons sur 14 dans l’exemple suivant (une valeur raisonnable en 2022).

BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(14);

Plus le facteur de travail est élevé, plus l’empreinte est robuste, mais plus l’exécution consomme de ressources CPU et de temps ! Sur ma machine, une exécution de BCrypt avec une charge de travail de 10 prend en moyenne environ 89 ms. Avec une charge de travail de 14, elle prend en moyenne 1 033 ms, soit près de 12 fois plus longtemps. Ce n’est pas un benchmark précis, mais cela montre bien l’impact de l’augmentation du facteur de travail.

Bonnes pratiques pour le hachage des mots de passe

Lorsque vous devez implémenter le hachage des mots de passe dans votre application, gardez à l’esprit quelques bonnes pratiques.

  1. N’implémentez jamais vous-même des algorithmes de hachage de mots de passe : utilisez plutôt une bibliothèque de développement éprouvée ! La cryptographie est un domaine complexe, et de nombreux problèmes peuvent survenir si vous tentez d’implémenter vous-même un algorithme courant.

  2. Les ordinateurs gagnant en puissance et en rapidité chaque année, les algorithmes de hachage des mots de passe et leurs paramètres doivent évoluer ! Les algorithmes modernes reposent sur vous, en tant que développeur, pour définir les ressources à utiliser lors du calcul d’une empreinte. Avec le temps, vous devrez donc mettre ces paramètres à jour. Pour rester au fait de ces évolutions, il est indispensable de suivre l’actualité et les tendances en matière de sécurité. Le blog Snyk, par exemple, est une excellente source d’informations sur la sécurité et ses évolutions.

Vérifiez la sécurité des bibliothèques et des méthodes que vous utilisez à l’aide d’outils gratuits comme Snyk, qui vous aident à détecter de manière proactive les problèmes de sécurité dans votre code. Des outils comme Snyk peuvent repérer ces problèmes à un stade précoce, proposer des correctifs et vous fournir des informations complémentaires. Ils jouent un rôle essentiel dans la sécurité durable de votre base de code, à mesure que les technologies évoluent.

Lancez-vous dans les compétitions Capture The Flag

Apprenez à résoudre des défis Capture The Flag en regardant à la demande notre atelier virtuel d’initiation.

Lire la suite

blog feature ai violet
Blog

VulnBump : les agents IA peuvent-ils corriger les dépendances vulnérables sans casser le build ?

VulnBump évalue des agents IA sur 307 mises à niveau de dépendances réelles. Une correction n’est comptabilisée que si la vulnérabilité disparaît et que la compilation et les tests restent au vert.

blog feature snyk container custom base recommendations
Blog

Découvrez Evo Agentic AppSec : une sécurité au rythme du développement de l’IA

Le développement piloté par l’IA dépasse les workflows de sécurité traditionnels. L’AppSec agentique de Snyk s’appuie sur des agents de sécurité pour détecter, corriger et vérifier les vulnérabilités en continu, tandis que les équipes définissent les exigences.

Blog

Les modèles de pointe ont trouvé les vulnérabilités. Seul l’attaquant a trouvé les chaînes d’exploitation.

L’analyse statique a détecté les failles, mais seuls des tests d’attaque en conditions réelles ont prouvé comment elles pouvaient être enchaînées pour provoquer des compromissions. Comparaison d’Evo COS, de Claude Security et de Claude Code Security.