Search code examples
spring-bootsecuritycsrfhttp-status-code-401

Why do I get a 401 error when I enable csrf?


So I am working on my first full-stack application (spring boot rest API and Vue.js frontend) and I came across a problem by using sonarqube. My sonarqube gives the following warning:

Make sure disabling Spring Security's CSRF protection is safe here.

and it is coming from this file:

@Configuration
@AllArgsConstructor
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class
WebSecurityConfig extends WebSecurityConfigurerAdapter {//provides security for endpoints

    @Autowired
    private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;

    @Autowired
    private UserDetailsService jwtUserDetailsService;

    @Autowired
    private JwtRequestFilter jwtRequestFilter;

    private final AccountService accountService;
    private final BCryptPasswordEncoder bCryptPasswordEncoder;

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        // configure AuthenticationManager so that it knows from where to load
        // user for matching credentials
        // Use BCryptPasswordEncoder
        auth.userDetailsService(jwtUserDetailsService).passwordEncoder(bCryptPasswordEncoder);
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .csrf()/*.disable()*/.and()//So we can send post requests without being rejected(if we using form based indication we want to enable this)
                .authorizeRequests()
                .antMatchers("/login", "/authenticate","/register", "/register/**")
                .permitAll()//any request that goes trough that end point we want to allow.
                .anyRequest()
                .authenticated().and().exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
                .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and().addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
        http.cors();
        http.logout().permitAll();
        http.logout().logoutSuccessHandler((new HttpStatusReturningLogoutSuccessHandler(HttpStatus.OK)));

    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(daoAuthenticationProvider());
    }

    @Bean
    public DaoAuthenticationProvider daoAuthenticationProvider() {
        DaoAuthenticationProvider provider =
                new DaoAuthenticationProvider();
        provider.setPasswordEncoder(bCryptPasswordEncoder);
        provider.setUserDetailsService(jwtUserDetailsService);
        return provider;
    }
}

More specifically this piece of code:

  @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                    .csrf()/*.disable()*/.and()//So we can send post requests without being rejected(if we using form based indication we want to enable this)
                    .authorizeRequests()
                    .antMatchers("/login", "/authenticate","/register", "/register/**")
                    .permitAll()//any request that goes trough that end point we want to allow.
                    .anyRequest()
                    .authenticated().and().exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
                    .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                    .and().addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
            http.cors();
            http.logout().permitAll();
            http.logout().logoutSuccessHandler((new HttpStatusReturningLogoutSuccessHandler(HttpStatus.OK)));

When I remove the first .and() and use disable (which is commented out now) my program works, but I want to find a solution where I can .csrf() let be enabled (I know it is standard enabled) and where my login stops giving me a 401 error.

Thanks in advance!


Solution

  • Apparently, you are using JWTs for authenticating requests. This typically does not involve cookies (tokens are usually sent as request headers). If this is the case for you (JWT is received in a header) you can disregard the Sonarqube warning, you don't need CSRF protection.

    The reason for this is CSRF is an attack where the attacker exploits the existing session of a victim user, when the victim visits a malicious website. This is based on cookies being sent with a request by the browser only depend on the destination, and not the origin (ie. a javascript on attacker.com can make a request to victim.com, and a user's cookies for victim.com will be sent automatically). If request headers are used to transmit a token, that cannot be accessed by an attacker on their malicious domain.

    If you still wanted to make it work (because for example your JWTs are indeed received from a cookie), you would have to send the correct CSRF token from your frontend (VueJS) with every request that's not a get, so it's a change for your frontend, not your backend.