Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 37

Sécurisation des mots de passe avec BCryptPasswordEncoder

Recommended resources

Bonjour ! Dans la leçon précédente, nous avons mis en place l'autorisation basée sur les rôles avec @PreAuthorize, pour contrôler ce que les utilisateurs authentifiés ont le droit de faire. Vous savez maintenant sécuriser vos endpoints.

Aujourd'hui, nous allons nous attaquer à une autre pierre angulaire de la sécurité : la protection des données au repos. Spécifiquement, l'une des données les plus critiques que vous puissiez stocker : le mot de passe de l'utilisateur.

L'objectif de cette leçon est d'apprendre à stocker les mots de passe de manière sécurisée en utilisant BCryptPasswordEncoder. À la fin, vous saurez comment hacher les mots de passe avant de les sauvegarder en base de données, et comment les vérifier lors de l'authentification, une pratique non négociable dans le développement d'applications professionnelles.

1. Pourquoi ne jamais stocker un mot de passe en clair ?

Stocker les mots de passe des utilisateurs en texte brut dans votre base de données est une faute professionnelle majeure. En cas de fuite de données (et cela arrive même aux plus grandes entreprises), tous les mots de passe de vos utilisateurs seraient exposés. Pour éviter cette catastrophe, on utilise une technique appelée le hachage.

Fonction de hachage: chiffrement du mot de passe
Ce schéma illustre parfaitement le concept. Le mot de passe de l'utilisateur n'est jamais stocké directement. Il passe par un "Générateur de hachage" qui le transforme en une chaîne de caractères complexe et non réversible (un "hash"). C'est ce hash qui est enregistré dans la base de données.

Le hachage est une fonction à sens unique : il est facile de calculer un hash à partir d'un mot de passe, mais quasiment impossible de retrouver le mot de passe original à partir du hash.

Il existe de nombreux algorithmes de hachage (MD5, SHA-256, etc.), mais pour les mots de passe, la norme est d'utiliser un algorithme adaptatif comme BCrypt. "Adaptatif" signifie que sa "lenteur" peut être ajustée. À mesure que la puissance de calcul des ordinateurs augmente, on peut augmenter le "coût" de calcul de BCrypt pour qu'il reste résistant aux attaques par force brute.

Spring Security fournit une implémentation prête à l'emploi : BCryptPasswordEncoder.


2. Étape 1 : Mettre le PasswordEncoder à disposition de Spring

Pour utiliser BCryptPasswordEncoder dans notre application, nous devons en déclarer une instance comme un Bean Spring. Cela permettra à Spring de l'injecter là où nous en avons besoin et, plus important encore, de l'intégrer dans son propre flux d'authentification.

Vous avez probablement déjà croisé ce Bean dans les leçons précédentes. Prenons le temps de bien comprendre son rôle dans la configuration de sécurité.

Sécurisez vos API avec Spring Security : JWT

L'article "Sécurisez vos API avec Spring Security : JWT" de sfeir.dev présente une classe de configuration très claire. Concentrez-vous sur la définition du Bean BCryptPasswordEncoder et sur la manière dont il est utilisé par l' AuthenticationProvider.

Lisez la section "Un peu de configuration globale". Portez une attention particulière à la description des Beans BCryptPasswordEncoder et AuthenticationProvider. Comprenez comment authProvider.setPasswordEncoder(passwordEncoder()) lie les deux ensemble.

Voici à quoi ressemble ce Bean, généralement placé dans votre classe de configuration de sécurité :

@Configuration
public class ApplicationConfiguration {

    // ... autres beans (UserDetailsService, etc.)

    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public AuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
        authProvider.setUserDetailsService(userDetailsService()); // Votre service qui charge l'utilisateur
        authProvider.setPasswordEncoder(passwordEncoder()); // On indique à Spring d'utiliser BCrypt
        return authProvider;
    }
    
    // ...
}

Une fois ce Bean déclaré, Spring Security l'utilisera automatiquement pour la vérification des mots de passe lors de l'authentification.


3. Étape 2 : Hacher le mot de passe lors de l'inscription

Maintenant que notre PasswordEncoder est disponible, nous devons l'utiliser activement chaque fois qu'un utilisateur est créé ou que son mot de passe est mis à jour. Le processus est simple :

  1. Injectez le PasswordEncoder dans votre service (par exemple, UserService ou AuthenticationService).
  2. Avant de sauvegarder l'entité User, appelez la méthode encode() sur le mot de passe fourni en clair.
  3. Définissez ce nouveau mot de passe haché sur l'entité avant de la persister.

Springboot RestAPI - Developpez.com

Le tutoriel de Developpez.com fournit un excellent exemple de code complet dans un UserServiceImpl. Cet extrait est particulièrement pertinent car il gère à la fois la création et la mise à jour d'un utilisateur.

Dans la ressource, trouvez la section intitulée "lmplémentation de l'interface UserService avec hachage du mot de passe utilisateur". Étudiez la méthode saveOrUpdateUser(User user). Observez comment bCryptPasswordEncoder.encode(user.getPassword()) est utilisé lors de la création d'un utilisateur (if(null == user.getId())).

Note importante : Le tutoriel de Developpez.com mentionne que BCrypt utilise l'algorithme SHA-1. C'est une erreur factuelle. BCrypt est un algorithme distinct, spécifiquement conçu pour le hachage de mots de passe et bien plus robuste que SHA-1 à cet effet.

Pour une démonstration visuelle de ce processus, la vidéo suivante est très directe.

Login et Inscription avec Spring Boot | Spring Security | MySQL

La vidéo "Login et Inscription avec Spring Boot" de Coding With Armand montre concrètement comment injecter et utiliser le PasswordEncoder dans un contrôleur d'inscription.

Regardez ces deux courts segments : De 13:09 à 13:50 : Création du Bean passwordEncoder. De 18:08 à 19:33 : Injection du PasswordEncoder dans le contrôleur et utilisation de la méthode encoder.encode() avant de sauvegarder l'utilisateur. C'est l'étape pratique à reproduire dans votre propre code.


4. Étape 3 : Vérifier le mot de passe lors de la connexion

La vérification est la contrepartie du hachage. Quand un utilisateur tente de se connecter, il envoie son mot de passe en clair. Nous devons le comparer au hash stocké en base de données.

Attention : N'essayez PAS de hacher le mot de passe fourni et de comparer les deux hashs !
bcrypt.encode("password123") ne produira pas toujours la même sortie, car BCrypt intègre un "sel" (salt) aléatoire dans le hash pour le renforcer.

La bonne méthode est d'utiliser passwordEncoder.matches(motDePasseEnClair, hashStocké). Cette méthode est conçue pour extraire le sel du hash stocké, l'appliquer au mot de passe en clair, et ensuite comparer les résultats.

Heureusement, si vous avez correctement configuré l'AuthenticationProvider (comme vu à l'étape 2), Spring Security s'en occupe pour vous !

Spring Security Authentication Flow with PasswordEncoder
Ce diagramme montre le flux d'authentification. L'`AuthenticationManager` délègue à l'`AuthenticationProvider`. Celui-ci utilise le `UserDetailsService` pour trouver l'utilisateur (et son hash de mot de passe) et le `PasswordEncoder` pour comparer le mot de passe soumis (`Input password`) avec celui de la base de données (`Database password`) via la méthode `matches()`.

L'extrait vidéo suivant explique comment le AuthenticationManager est configuré pour utiliser notre PasswordEncoder et UserDetailsService afin d'effectuer cette vérification.

Login et Inscription avec Spring Boot | Spring Security | MySQL

Ce segment de la vidéo de Coding With Armand explique comment les différents composants que nous avons configurés (UserDetailsService, PasswordEncoder) sont assemblés pour permettre au AuthenticationManager de valider les identifiants de l'utilisateur.

Regardez la partie de 13:50 à 16:28. Le présentateur explique la nécessité de vérifier le mot de passe et configure l' AuthenticationManager pour qu'il utilise le UserDetailsService et le PasswordEncoder que nous avons définis. Cela vous donne une vision complète du puzzle de l'authentification.


Conclusion

Vous venez de mettre en œuvre l'une des pratiques de sécurité les plus fondamentales. En hachant systématiquement les mots de passe, vous protégez vos utilisateurs même en cas de compromission de votre base de données.

Points clés à retenir :

  • Ne jamais stocker de mots de passe en clair. Utilisez toujours une fonction de hachage forte et adaptative comme BCrypt.
  • Configurez BCryptPasswordEncoder comme un @Bean dans votre configuration Spring Security et liez-le à votre AuthenticationProvider.
  • Utilisez la méthode passwordEncoder.encode(plainTextPassword) pour hacher les mots de passe avant de les enregistrer lors de la création ou de la mise à jour d'un utilisateur.
  • Laissez Spring Security gérer la vérification via la méthode matches() en arrière-plan lors du processus de connexion.

Dans la prochaine et dernière leçon de ce module sur la sécurité, nous aborderons la protection contre les attaques de type CSRF (Cross-Site Request Forgery). Nous analyserons son utilité dans le contexte d'une API REST stateless et verrons comment la configurer correctement.