Search code examples
javaspring-bootcsrfcsrf-token

Spring Boot -- Post request with CSRF token produce 403 error


I'm trying to implement CSRF token security in my Spring Boot API to learn how to deal with that.

I've followed this tutorial (server side part) and this is my security config:

private static final String[] CSRF_IGNORE = {"/api/login"};


protected void configure(HttpSecurity http) throws Exception {
        http
                .csrf()
                .ignoringAntMatchers(CSRF_IGNORE)
                .csrfTokenRepository(csrfTokenRepository())
                .and()
                .addFilterAfter(new CustomCsrfFilter(), CsrfFilter.class)
                .exceptionHandling()
                .authenticationEntryPoint(new Http403ForbiddenEntryPoint() {
                })
                .and()
                .authenticationProvider(getProvider())
                .formLogin()
                .loginProcessingUrl("/api/login")
                .successHandler(new AuthentificationLoginSuccessHandler())
                .failureHandler(new SimpleUrlAuthenticationFailureHandler())
                .and()
                .logout()
                .logoutUrl("/api/logout")
                .logoutSuccessHandler(new AuthentificationLogoutSuccessHandler())
                .invalidateHttpSession(true)
                .and()
                .authorizeRequests()
                .anyRequest().authenticated();
    }

Others things are the same as in the tutorial.

I'm testing with Postman.

When i add the endpoint i want in CSRF_IGNORE, i can see with logger/debug that token stocked, and token from cookie are the same, because the security config's part CustomCsrfFilter.java in .addFilterAfter() is used, but when i remove the endpoint from this CSRF_IGNORE, what i get is a 403, and, logger/debug in the CustomCsrfFilter.java isn't used, so i'm thinking that tokens aren't compared.

I think I missed something and I would like to understand.


Solution

  • If you want to use CSRF with a http only false cookie, why not use Spring Security's built in CookieCsrfTokenRepository? Should simplify your config that way. CustomCsrfFilter seems to be adding a XSRF-TOKEN cookie to the HttpServletResponse, which CookieCsrfTokenRepository does for you.

    The default CSRF cookie name when using CookieCsrfTokenRepository is X-CSRF-TOKEN, which is conveniently the default name Angular's HttpClientXsrfModule uses. Of course you can customize that if you need.

    So your security config becomes:

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                    .csrf()
                        .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
                    .and()
                    .exceptionHandling()
                        .authenticationEntryPoint(new Http403ForbiddenEntryPoint())
                    .and()
                        .authenticationProvider(getProvider())
                    .formLogin()
                        .loginProcessingUrl("/api/login")
                        .successHandler(new AuthentificationLoginSuccessHandler())
                        .failureHandler(new SimpleUrlAuthenticationFailureHandler())
                    .and()
                    .logout()
                        .logoutUrl("/api/logout")
                        .logoutSuccessHandler(new AuthentificationLogoutSuccessHandler())
                        .invalidateHttpSession(true)
                    .and()
                    .authorizeRequests()
                        .anyRequest().authenticated();
        }
    

    And with Angular, your app module has HttpClientXsrfModule as

    @NgModule({
      declarations: [
        AppComponent
      ],
      imports: [
        BrowserModule,
        HttpClientModule,
        HttpClientXsrfModule
      ],
      providers: [],
      bootstrap: [AppComponent]
    })
    export class AppModule { }