I am struggling with implementing CSRF into my application. This is my current security layout:
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests(authorization -> authorization
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.requestMatchers("/api/moderator/**").hasAnyRole("ADMIN", "MODERATOR")
.requestMatchers("/api/orders/**").authenticated()
.requestMatchers("/api/**").permitAll()
.anyRequest().authenticated())
.csrf().disable()
.addFilterBefore(jwtFilter(), UsernamePasswordAuthenticationFilter.class)
.build();
}
If I remove .csrf().disable()
all POST-requests are blocked with a 403 Forbidden. As far as I understand, this is to protect the server from POST-requests not coming from my client.
So how do I safely implement CSRF protection to my server, and 'prove' to it requests are coming from a safe source?
UPDATE
I managed to add CSRF like this, which returns a XSRF-TOKEN to my postman:
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests(authorization -> authorization
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.requestMatchers("/api/moderator/**").hasAnyRole("ADMIN", "MODERATOR")
.requestMatchers("/api/orders/**").authenticated()
.requestMatchers("/api/**").permitAll()
.anyRequest().authenticated())
.addFilterBefore(jwtFilter(), UsernamePasswordAuthenticationFilter.class)
.csrf((csrf) -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()))
.build();
}
I then added a new header to the postman request, calling it X-XSRF-TOKEN (as instructed by the Spring documentation), and giving it the value provided by the server, but I am still getting a 403, and the server logs says: Invalid CSRF token found for http://localhost:8080/api/login
Try this:
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.csrfTokenRequestHandler(new CsrfTokenRequestAttributeHandler()))
// ...
return http.build();
}
By default, Spring implementation uses XorCsrfTokenRequestAttributeHandler
, which is designed to protect against breach attacks. This handler expects CSRF tokens to be encoded in HTML form requests, making it unsuitable for typical REST API use cases where CSRF tokens are sent via request headers.
If you explicitly specify this handler it should potentially allow Spring Security to expect CSRF tokens in the request headers, bypassing the need for encoding and thereby avoiding the 403 error.
Additionally you can read this thread to get more context: CSRF protection not working with Spring Security 6