Protection CSRF avec Spring MVC et Thymeleaf

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 àthis writeup.

Dans cet article, nous allons expliquer commentprevent Cross-Site Request Forgery (CSRF) attacks 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 actuellement authentifiée.

2. Dépendances Maven

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


    org.thymeleaf
    thymeleaf
    3.0.9.RELEASE


    org.thymeleaf
    thymeleaf-spring4
    3.0.9.RELEASE

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

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


    org.springframework.security
    spring-security-web
    5.0.6.RELEASE


    org.springframework.security
    spring-security-config
    5.0.6.RELEASE

Les dernières versions de deux bibliothèques liées à Spring Security sont disponibleshere ethere.

3. Configuration Java

En plus de la configuration Thymeleaf couvertehere, 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érieSecurity with Spring.

CSRF protection is enabled by default with Java configuration. Afin de désactiver cette fonctionnalité utile, nous devons l'ajouter dans la méthodeconfigure(…):

.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 à saisie 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


    

Add Student

Dans cette vue, nous ajoutons un étudiant à la liste, en fournissantid,name,gender etpercentage (facultativement, comme indiqué dans le formulaire de validation). Avant de pouvoir exécuter ce formulaire, nous devons fourniruser etpassword, 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 esthttp://localhost:8080/spring-thymeleaf/saveStudent. Le pirate veut accéder à cette page pour lancer une attaque.

Afin de 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:

image

 

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

Veuillez noter que la session HTTP est utilisée pour stocker le jeton CSRF. Lorsque la demande est envoyée, Spring compare le jeton généré au 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 passons 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 entraînera un statut d'interdiction en raison du jeton CSRF manquant, alors que le second sera exécuté correctement.

5. Conclusion

Dans cet article, nous avons expliqué comment prévenir les attaques CSRF à l'aide du cadre Spring Security et Thymeleaf.

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