Protection CSRF avec Spring MVC et Thymeleaf

1. Introduction

Thymeleaf est un moteur de modèle Java pour le traitement et la création de HTML, XML, JavaScript, CSS et texte brut. Pour une introduction à Thymeleaf et Spring, jetez un œil à cet article.

Dans cet article, nous verrons comment empêcher les attaques de falsification de requête intersite (CSRF) dans Spring MVC avec l'application Thymeleaf. Pour être plus précis, nous allons tester l'attaque CSRF pour la méthode HTTP POST.

CSRF est une attaque qui oblige un utilisateur final à exécuter des actions indésirables dans une application Web dans laquelle est actuellement authentifié.

2. Dépendances de Maven

Voyons d'abord les configurations requises pour intégrer Thymeleaf avec Spring. La bibliothèque thymeleaf-spring est requise dans nos dépendances:

 org.thymeleaf thymeleaf 3.0.11.RELEASE   org.thymeleaf thymeleaf-spring5 3.0.11.RELEASE 

Notez que, pour un projet Spring 4, la bibliothèque thymeleaf-spring4 doit être utilisée à la place de thymeleaf-spring5 . La dernière version des dépendances peut être trouvée ici.

De plus, pour utiliser Spring Security, nous devons ajouter les dépendances suivantes:

 org.springframework.security spring-security-web 5.2.3.RELEASE   org.springframework.security spring-security-config 5.2.3.RELEASE  

Les dernières versions de deux bibliothèques liées à Spring Security sont disponibles ici et ici.

3. Configuration Java

En plus de la configuration Thymeleaf abordée ici, nous devons ajouter une configuration pour Spring Security. Pour ce faire, nous devons créer la classe:

@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true) public class WebMVCSecurity extends WebSecurityConfigurerAdapter { @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() .withUser("user1").password("{noop}user1Pass") .authorities("ROLE_USER"); } @Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers("/resources/**"); } @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .anyRequest() .authenticated() .and() .httpBasic(); } }

Pour plus de détails et une description de la configuration de la sécurité, nous nous référons à la série Security with Spring.

La protection CSRF est activée par défaut avec la configuration Java. Afin de désactiver cette fonctionnalité utile, nous devons l'ajouter dans la méthode configure (…) :

.csrf().disable()

Dans la configuration XML, nous devons spécifier la protection CSRF manuellement, sinon cela ne fonctionnera pas:

Veuillez également noter que si nous utilisons une page de connexion avec un formulaire de connexion, nous devons toujours inclure le jeton CSRF dans le formulaire de connexion en tant que paramètre masqué manuellement dans le code:

Pour les formulaires restants, le jeton CSRF sera automatiquement ajouté aux formulaires avec une entrée masquée:

4. Configuration des vues

Passons à la partie principale des fichiers HTML avec les actions de formulaire et la création de procédures de test. Dans la première vue, nous essayons d'ajouter un nouvel étudiant à la liste:

   Add Student   
    

Dans cette vue, nous ajoutons un étudiant à la liste, en fournissant son identifiant , son nom , son sexe et son pourcentage (éventuellement, comme indiqué dans la validation du formulaire). Avant de pouvoir exécuter ce formulaire, nous devons fournir un utilisateur et un mot de passe pour nous authentifier dans une application Web.

4.1. Test d'attaque CSRF du navigateur

Nous passons maintenant à la deuxième vue HTML. Le but est d'essayer de faire une attaque CSRF:

Nous savons que l'URL de l'action est // localhost: 8080 / spring-thymeleaf / saveStudent . Le hacker veut accéder à cette page pour effectuer une attaque.

Pour tester, ouvrez le fichier HTML dans un autre navigateur, sans vous connecter à l'application. Lorsque vous essayez de soumettre le formulaire, nous recevrons la page:

Notre demande a été refusée car nous avons envoyé une demande sans jeton CSRF.

Veuillez noter que cette session HTTP est utilisée pour stocker le jeton CSRF. Lorsque la demande est envoyée, Spring compare le jeton généré avec le jeton stocké dans la session, afin de confirmer que l'utilisateur n'est pas piraté.

4.2. Test d'attaque JUnit CSRF

Si vous ne souhaitez pas tester l'attaque CSRF à l'aide d'un navigateur, vous pouvez également le faire via un test d'intégration rapide; commençons par la configuration Spring pour ce test:

@RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration @ContextConfiguration(classes = { WebApp.class, WebMVCConfig.class, WebMVCSecurity.class, InitSecurity.class }) public class CsrfEnabledIntegrationTest { // configuration }

Et passez aux tests réels:

@Test public void addStudentWithoutCSRF() throws Exception { mockMvc.perform(post("/saveStudent").contentType(MediaType.APPLICATION_JSON) .param("id", "1234567").param("name", "Joe").param("gender", "M") .with(testUser())).andExpect(status().isForbidden()); } @Test public void addStudentWithCSRF() throws Exception { mockMvc.perform(post("/saveStudent").contentType(MediaType.APPLICATION_JSON) .param("id", "1234567").param("name", "Joe").param("gender", "M") .with(testUser()).with(csrf())).andExpect(status().isOk()); }

Le premier test aboutira à un statut interdit en raison du jeton CSRF manquant, tandis que le second sera exécuté correctement.

5. Conclusion

Dans cet article, nous avons expliqué comment empêcher les attaques CSRF à l'aide du framework Spring Security et Thymeleaf.

L'implémentation complète de ce didacticiel se trouve dans le projet GitHub - il s'agit d'un projet basé sur Eclipse, il devrait donc être facile à importer et à exécuter tel quel.