Proteção CSRF com Spring MVC e Thymeleaf

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

As versões mais recentes de duas bibliotecas relacionadas ao Spring Security estão disponíveishereehere.

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:

image

 

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