Closed
Description
Describe the bug
CsrfAuthenticationStrategy
does not check for an existing token and always saves a null
token when called. When used with CookieCsrfTokenRepository
, this results in three Set-Cookie
headers in the response. For example:
http :8080/login -a user:password
produces a response like:
HTTP/1.1 200
Set-Cookie: XSRF-TOKEN=e97cdf5e-0dbc-45d3-8720-d2731023c3fa; Path=/
Set-Cookie: XSRF-TOKEN=; Max-Age=0; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/
Set-Cookie: XSRF-TOKEN=0dec2144-d4e6-479e-8c56-e70e61423cb6; Path=/
...
Note: The fix for gh-12141 addressed a related problem, originally discussed in gh-12094.
To Reproduce
See sample.
http :8080/login -a user:password
- Observe that
POST /login
contains threeSet-Cookie
headers forXSRF-TOKEN
.
Expected behavior
Because the request did not contain an XSRF-TOKEN
cookie, only a single Set-Cookie
header containing the generated token should be returned in the response.
Sample
Spring Security Version: 5.8.0-SNAPSHOT (after 6b0ed02).
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().authenticated()
)
.csrf((csrf) -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
)
.httpBasic(Customizer.withDefaults());
// @formatter:on
return http.build();
}
@Bean
public UserDetailsService userDetailsService() {
// @formatter:off
UserDetails userDetails = User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build();
// @formatter:on
return new InMemoryUserDetailsManager(userDetails);
}
}