Reputation: 1316
As I am no security expert, I'm here to reach out too you again for advice reguarding CSRF handling, in my case through Spring boot configuration.
I have the following Setup:
Security Config
@EnableWebSecurity
public class WebSecurityConfig {
private static final String ADMIN = "ADMIN";
private static final String USER = "USER";
@Bean
public InMemoryUserDetailsManager userDetailsService() {
UserDetails user = User
.withUsername("user")
.password(passwordEncoder().encode("password"))
.roles(USER)
.build();
UserDetails admin = User
.withUsername("admin")
.password(passwordEncoder().encode("password"))
.roles(ADMIN)
.build();
return new InMemoryUserDetailsManager(user, admin);
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
//@Order(1) // Order is required incase you create multiple filterchains for security
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.authorizeRequests()
.antMatchers("/auth/login").permitAll()
.antMatchers("/books/special").hasAnyRole("ADMIN")
.antMatchers("/books/**").hasAnyRole("USER", "ADMIN")
.anyRequest().authenticated()
.and()
.httpBasic()
.and()
.logout().logoutRequestMatcher(new AntPathRequestMatcher("/auth/logout"))
.and()
.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
.build();
}
}
RestControllers
@RestController
@RequestMapping("/auth")
public class AuthenticationController {
@GetMapping("/login")
public String login() {
return "You are logged in";
}
@PostMapping("/logout")
public String logout() {
return "You are logged out";
}
}
@RestController
@RequestMapping("/books")
public class BookController {
@GetMapping
public List<String> getAllBooks() {
return Arrays.asList("Book1", "Book2", "Book3", "Book4", "Book5");
}
@GetMapping("/special")
public String getSpecialBook() {
return "Special book";
}
@PostMapping
public String createBook() {
return "Book6";
}
@PatchMapping
public String updateBook() {
return "Book6 is now book7";
}
@DeleteMapping("/special")
public String deleteBook() {
return "Some book was deleted";
}
}
I'm currently using Postman to verify the authentication part acts as intended.
I'm getting the XSRF-TOKEN from the cookie generated by Spring security and i'm using it in my headers for POST / PATCH / DELETE (Not for GET methods)
POST / PATCH / DELETE works as intended, we get 403 forbidden if the X-XSRF-TOKEN is not present and when present I receive the strings from my controller.
Here comes the question where I'm getting a little confused.
It seems like as soon as my application has been authenticated with a user calling ex /login
The users authentication is saved somewhere and I can now access all GET endpoints without any further authentication. Even if I restart my application, I don't need to reauthenticate.
Only after specifically calling the "/logout" endpoint do I need to authenticate again when calling a GET request.
In spring docs the say we need to explicitly set X-XSRF-TOKEN for all action requests(POST/PUT/PATCH/DELETE), but they don't really mention how GET requests should be handled, except for
We can relax the expectations to only require the token for each HTTP request that updates state. This can be safely done since the same origin policy ensures the evil site cannot read the response. Additionally, we do not want to include the random token in HTTP GET as this can cause the tokens to be leaked.
I hope this is not too confusing to read as it was to write, and I'm happy if people can just provide me with theories so I can extend my research into this, because I feel like this could cause some vulnerabilities that I have yet to discover.
Kind regards
Upvotes: 1
Views: 809
Reputation: 1316
Mr. Deinum more or less answered my question in the comments.
The reason for my apps behavior was that the SessionManagement wasn't set to stateless, hence a session was kept for the logged in used and was automatically sent from postman on each request.
To authenticate on each request I needed to add the following line to my WebSecurityConfig:
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
This removed the JSESSIONID cookie I was receiving and refreshed my CSRF token on each request.
Before my CSRF token was the same on each request probably due to the recognized session.
Thank you for your help solving the mystery!
Upvotes: 0