Bonjour ! Dans la leçon précédente, nous avons vu comment stocker les mots de passe de manière sécurisée en utilisant BCryptPasswordEncoder, une pratique essentielle pour protéger les données de vos utilisateurs.
Aujourd'hui, nous concluons notre module sur la sécurité en abordant une menace différente : le Cross-Site Request Forgery (CSRF). L'objectif de cette leçon est d'analyser l'utilité de la protection CSRF dans le contexte d'une API REST stateless et de la configurer.
Vous pourriez penser que les attaques CSRF, traditionnellement associées aux applications web basées sur les sessions, ne concernent pas les API stateless utilisant des JWT. Nous allons voir que la réalité est plus nuancée. Comprendre cette nuance est crucial pour construire des applications réellement robustes, une compétence clé sur le marché du travail international que vous visez.
1. Qu'est-ce qu'une attaque CSRF ?
Avant de plonger dans la configuration, assurons-nous de bien comprendre la menace. Une attaque CSRF (ou "falsification de requête inter-sites") consiste à tromper un utilisateur authentifié pour qu'il exécute à son insu une action sur une application web. L'attaquant exploite le fait que le navigateur de la victime envoie automatiquement les cookies d'authentification avec chaque requête vers le site concerné.
Pour une explication claire et un exemple pratique en français, je vous propose de visionner le début de la vidéo suivante.
Sécuriser ses applications web : Attaques CSRF
Cette vidéo de Grafikart.fr explique brillamment le principe d'une attaque CSRF avec une démonstration simple et efficace.
Regardez le segment de 00:00 à 01:14. Concentrez-vous sur le scénario : un site malveillant force le navigateur d'un utilisateur connecté à un autre site à effectuer une action (changer de pseudo) sans son consentement.
En résumé, l'attaque fonctionne parce que :
- L'utilisateur est authentifié sur le site cible (ex:
ma-banque.com), et son navigateur a stocké un cookie de session. - L'utilisateur visite un site malveillant (
site-malveillant.com). - Ce site contient un formulaire ou un script qui envoie une requête vers
ma-banque.com(ex: pour effectuer un virement). - Le navigateur, voyant une requête vers
ma-banque.com, y joint automatiquement le cookie de session. - Pour
ma-banque.com, la requête semble légitime car elle vient avec un cookie de session valide. L'action est exécutée.
2. Le dilemme : JWT, XSS et CSRF
Dans notre architecture stateless avec JWT, nous n'utilisons pas de cookie de session. Le token est envoyé dans l'en-tête Authorization. Dans ce cas, nous sommes naturellement protégés du CSRF, car un site tiers ne peut pas forcer le navigateur à ajouter cet en-tête. C'est pourquoi de nombreux tutoriels conseillent de simplement désactiver la protection CSRF de Spring Security :
// Extrait d'une configuration de sécurité typique pour une API JWT
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.csrf(AbstractHttpConfigurer::disable) // <- La ligne souvent recommandée
// ... reste de la configuration
.build();
}
Vous trouverez cette approche dans l'article de sfeir.dev (resource_id: [LINK](https://www.sfeir.dev/back/securisez-vos-api-avec-spring-security-jwt/)) que nous avons consulté pour la configuration JWT.
Alors, où est le problème ?
Le problème réside dans le stockage du JWT côté client. Comme nous l'avons évoqué, si vous stockez le JWT dans le localStorage du navigateur (une pratique courante), il est vulnérable aux attaques XSS (Cross-Site Scripting). Un script malveillant injecté sur votre site pourrait lire le token depuis le localStorage et le voler.
Pour se prémunir du XSS, la meilleure pratique est de stocker le JWT dans un cookie HttpOnly. Ce type de cookie ne peut pas être lu par JavaScript, ce qui neutralise le vol de token via XSS. Mais en faisant cela... nous rendons notre application de nouveau vulnérable au CSRF, car les cookies HttpOnly sont, eux aussi, envoyés automatiquement par le navigateur !
C'est le dilemme :
localStorage: Vulnérable au XSS.- Cookie
HttpOnly: Vulnérable au CSRF.
L'article suivant, en français, explique ce dilemme et introduit la solution que nous allons adopter.
Sécuriser une API REST (3/3) : gestion du JWT coté client
L'article "Sécuriser une API REST (3/3)" de codeheroes.fr expose clairement le conflit entre la protection XSS et CSRF lors du stockage de JWT et propose une solution hybride.
Lisez les sections "La faille CSRF" et "FUUUUSION… HA !". L'objectif est de comprendre : Pourquoi le stockage du JWT dans un cookie expose à nouveau au CSRF. Le principe de la solution : diviser l'information d'authentification en deux parties, l'une dans un cookie et l'autre accessible par le client, qui doivent être présentées ensemble pour valider la requête.
3. La solution : Le "Double Submit Cookie Pattern"
La solution à ce dilemme est une technique appelée Double Submit Cookie. Elle est parfaitement adaptée à nos API stateless et Spring Security la supporte nativement.
Le principe est le suivant :
- En plus du cookie
HttpOnlycontenant le JWT, le serveur génère un second cookie contenant un token CSRF. Ce cookie-là n'est pasHttpOnly, il doit être lisible par le code JavaScript du frontend. - Pour chaque requête qui modifie l'état de l'application (POST, PUT, DELETE, PATCH), le client (votre application React) doit :
- Lire la valeur du token CSRF depuis le cookie.
- Ajouter cette valeur dans un en-tête HTTP (par convention,
X-XSRF-TOKEN).
- Le serveur, à la réception de la requête, vérifie deux choses :
- La présence et la validité du cookie CSRF.
- La présence de l'en-tête
X-XSRF-TOKENet que sa valeur correspond à celle du cookie.
Un attaquant ne peut pas contourner cette protection. Même s'il peut forcer le navigateur à envoyer le cookie CSRF, il ne peut pas lire sa valeur pour la copier dans l'en-tête X-XSRF-TOKEN à cause de la politique de même origine (Same-Origin Policy) des navigateurs.
4. Configuration avec Spring Security
Maintenant, la pratique. Au lieu de désactiver CSRF, nous allons le configurer pour qu'il utilise cette stratégie.
La vidéo suivante, bien qu'en anglais, montre très clairement la configuration de Spring Security et l'interaction avec un client React. Le code est universel et la démonstration visuelle est très parlante.
The CSRF Protection with Spring Security | Spring Boot Backend #3.5
La vidéo "The CSRF Protection with Spring Security" de The Dev World montre pas à pas comment implémenter la protection CSRF pour une API stateless Spring Boot et une application React.
Regardez les segments suivants : Configuration de Spring Security (03:26 - 06:02) : Observez comment .csrf() est configuré avec CookieCsrfTokenRepository.withHttpOnlyFalse(). C'est la ligne clé. Notez aussi qu'il faut autoriser l'en-tête XSRF-TOKEN et la méthode OPTIONS pour que CORS fonctionne. Démonstration Pratique (06:02 - 07:17) : Le présentateur montre dans les outils de développement du navigateur comment le cookie XSRF-TOKEN est reçu, et comment sa valeur est ensuite envoyée dans l'en-tête X-XSRF-TOKEN lors de la requête de connexion. Récapitulatif (07:17 - 07:52) : Un excellent résumé de toutes les étapes de configuration.
Voici le code de configuration crucial à ajouter dans votre SecurityFilterChain :
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler;
// ...
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
// Ce gestionnaire garantit que le token est chargé de manière "lazy"
CsrfTokenRequestAttributeHandler requestHandler = new CsrfTokenRequestAttributeHandler();
requestHandler.setCsrfRequestAttributeName(null);
return http
// ... configuration de sessionManagement, authorizeHttpRequests, etc.
.csrf(csrf -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.csrfTokenRequestHandler(requestHandler)
)
// ... reste de la configuration (addFilterBefore, etc.)
.build();
}
Points importants :
CookieCsrfTokenRepository.withHttpOnlyFalse(): Indique à Spring de stocker le token CSRF dans un cookie (nomméXSRF-TOKENpar défaut) et de le rendre lisible par JavaScript (HttpOnlyàfalse).CsrfTokenRequestAttributeHandler: Cette configuration (surtoutsetCsrfRequestAttributeName(null)) est une bonne pratique avec les API stateless pour éviter que Spring ne cherche le token CSRF dans les attributs de la requête serveur, optimisant légèrement le processus.- CORS : N'oubliez pas de vous assurer que votre configuration CORS autorise l'en-tête
X-XSRF-TOKEN.
Côté frontend (React), la plupart des librairies HTTP comme axios gèrent cela automatiquement. Si vous utilisez axios, il lira par défaut le cookie XSRF-TOKEN et placera sa valeur dans l'en-tête X-XSRF-TOKEN pour les requêtes cross-site.
Conclusion
Félicitations ! Vous avez terminé le module sur la sécurité. Vous êtes maintenant capable de sécuriser une API REST Spring Boot de bout en bout.
Points clés à retenir sur le CSRF :
- La protection CSRF n'est pas inutile pour une API stateless si vous stockez le JWT dans un cookie
HttpOnlypour vous protéger du XSS. - Le Double Submit Cookie Pattern est la solution standard pour ce scénario.
- Spring Security facilite grandement sa mise en place avec
CookieCsrfTokenRepository.withHttpOnlyFalse(). - Cette protection nécessite une coopération entre le backend (qui fournit le token dans un cookie lisible) et le frontend (qui lit le token et le renvoie dans un en-tête).
Dans le prochain module, Architecture Logicielle et Design Patterns, nous prendrons de la hauteur. Nous commencerons par structurer notre application en couches (Présentation, Métier, Données) avec Spring, une base essentielle pour construire des applications maintenables et évolutives.