Proteção CSRF com Spring MVC e Thymeleaf
1. Introdução
Thymeleaf é um mecanismo de template Java para processar e criar HTML, XML, JavaScript, CSS e texto simples. Para uma introdução de Thymeleaf e Spring, dê uma olhada emthis writeup.
Neste artigo, discutiremos comoprevent Cross-Site Request Forgery (CSRF) attacks no Spring MVC com o aplicativo Thymeleaf. Para ser mais específico, testaremos o ataque CSRF para o método HTTP POST.
O CSRF é um ataque que força um usuário final a executar ações indesejadas em um aplicativo Web no qual está atualmente autenticado.
2. Dependências do Maven
Primeiro, vamos ver as configurações necessárias para integrar o Thymeleaf ao Spring. A bibliotecathymeleaf-spring é necessária em nossas dependências:
org.thymeleaf
thymeleaf
3.0.9.RELEASE
org.thymeleaf
thymeleaf-spring4
3.0.9.RELEASE
Observe que, para um projeto Spring 4, a bibliotecathymeleaf-spring4 deve ser usada em vez dethymeleaf-spring5. A versão mais recente das dependências pode ser encontradahere.
Além disso, para usar o Spring Security, precisamos adicionar as seguintes dependências:
org.springframework.security
spring-security-web
5.0.6.RELEASE
org.springframework.security
spring-security-config
5.0.6.RELEASE
3. Configuração Java
Além da configuração do Thymeleaf coberta porhere, precisamos adicionar configuração para Spring Security. Para fazer isso, precisamos criar a 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();
}
}
Para obter mais detalhes e descrição da configuração de segurança, consulte a sérieSecurity with Spring.
CSRF protection is enabled by default with Java configuration. Para desativar este recurso útil, precisamos adicioná-lo ao métodoconfigure(…):
.csrf().disable()
Na configuração XML, precisamos especificar a proteção CSRF manualmente, caso contrário, ela não funcionará:
Observe também que, se estamos usando a página de login com o formulário de login, precisamos sempre incluir o token CSRF no formulário de login como um parâmetro oculto manualmente no código:
Para os formulários restantes, o token CSRF será automaticamente adicionado aos formulários com entrada oculta:
4. Configuração de visualizações
Vamos prosseguir para a parte principal dos arquivos HTML com ações de formulário e criação de procedimento de teste. Na primeira visualização, tentamos adicionar um novo aluno à lista:
Add Student
Add Student
Nesta visualização, estamos adicionando um aluno à lista, fornecendoid,name,genderepercentage (opcionalmente, conforme declarado na validação do formulário). Antes de podermos executar este formulário, precisamos forneceruserepassword, para nos autenticar em um aplicativo da web.
4.1. Teste de ataque CSRF do navegador
Agora vamos para a segunda visualização em HTML. O objetivo disso é tentar fazer um ataque CSRF:
Sabemos que o URL da ação éhttp://localhost:8080/spring-thymeleaf/saveStudent. O hacker deseja acessar esta página para realizar um ataque.
Para testar, abra o arquivo HTML em outro navegador, sem fazer login no aplicativo. Quando você tenta enviar o formulário, receberemos a página:
Nossa solicitação foi negada porque enviamos uma solicitação sem um token CSRF.
Observe que a sessão HTTP é usada para armazenar o token CSRF. Quando a solicitação é enviada, o Spring compara o token gerado com o token armazenado na sessão, para confirmar que o usuário não foi invadido.
4.2. Teste de ataque JUnit CSRF
Se você não quiser testar o ataque CSRF usando um navegador, também pode fazer isso por meio de um teste de integração rápido; vamos começar com a configuração do Spring para esse teste:
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = {
WebApp.class, WebMVCConfig.class, WebMVCSecurity.class, InitSecurity.class })
public class CsrfEnabledIntegrationTest {
// configuration
}
E vá para os testes reais:
@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());
}
O primeiro teste resultará em um status proibido devido ao token CSRF ausente, enquanto o segundo será executado corretamente.
5. Conclusão
Neste artigo, discutimos como impedir ataques de CSRF usando a estrutura Spring Security e Thymeleaf.
A implementação completa deste tutorial pode ser encontrada emthe GitHub project - este é um projeto baseado em Eclipse, portanto, deve ser fácil de importar e executar como está.