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
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:
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.