Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CookieCsrfTokenRepository overwrites previous Set-Cookie response headers #13075

Closed
mraible opened this issue Apr 23, 2023 · 6 comments
Closed
Assignees
Labels
in: web An issue in web modules (web, webmvc) type: bug A general bug
Milestone

Comments

@mraible
Copy link
Contributor

mraible commented Apr 23, 2023

Describe the bug

With Spring Boot 3.0.5, I have the following Security Configuration and CSRF works as expected.

package com.okta.developer.jugtours.config;

import com.okta.developer.jugtours.web.CookieCsrfFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler;

@Configuration
public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests((authz) -> authz
                .requestMatchers("/", "/api/user").permitAll()
                .anyRequest().authenticated()
            );

        http.oauth2Login();
        http.oauth2ResourceServer().jwt();

        http.csrf()
            .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
            .csrfTokenRequestHandler(new CsrfTokenRequestAttributeHandler());
        http.addFilterAfter(new CookieCsrfFilter(), BasicAuthenticationFilter.class);

        return http.build();
    }
}

If I upgrade to 3.1.0-RC1, it seems that CSRF causes issues, and I'm unable to login and see any endpoints (e.g. /api/groups) that are secured. It results in an endless redirect that eventually results in rate-limiting errors (from Auth0, in my case).

To Reproduce

Here's a repo that you can reproduce the problem with: https://github.com/oktadev/auth0-spring-boot-angular-crud-example

Instructions to reproduce:

  1. Clone the repo above.

     git clone https://github.com/oktadev/auth0-spring-boot-angular-crud-example
    
  2. Install the Auth0 CLI and run auth0 login in a terminal. Then, run auth0 apps create:

     auth0 apps create \
       --name "Spring Boot 3.1" \
       --description "So Bootiful" \
       --type regular \
       --callbacks http://localhost:8080/login/oauth2/code/okta \
       --logout-urls http://localhost:8080 \
       --reveal-secrets
    
  3. Copy the results from the CLI into an okta.env file:

     export OKTA_OAUTH2_ISSUER=https://<your-auth0-domain>/
     export OKTA_OAUTH2_CLIENT_ID=<your-client-id>
     export OKTA_OAUTH2_CLIENT_SECRET=<your-client-secret>
     export OKTA_OAUTH2_AUDIENCE=https://<your-auth0-domain>/api/v2/
    
  4. Start the app and log in:

     source okta.env
     mvn spring-boot:run
    

You'll get an infinite redirect when you try to hit http://localhost:8080/api/groups. If you disable CSRF, it will work. Also, if you modify pom.xml to use Spring Boot version 3.0.5, everything will work without disabling CSRF.

Expected behavior

Everything should work just fine with Spring Boot 3.1, as it does with Spring Boot 3.0.5.

Sample

https://github.com/oktadev/auth0-spring-boot-angular-crud-example

@mraible mraible added status: waiting-for-triage An issue we've not yet triaged type: bug A general bug labels Apr 23, 2023
@jzheaux jzheaux added in: web An issue in web modules (web, webmvc) and removed status: waiting-for-triage An issue we've not yet triaged labels Apr 24, 2023
@sjohnr sjohnr moved this to Prioritized in Spring Security Team Apr 25, 2023
@sjohnr
Copy link
Member

sjohnr commented Apr 25, 2023

@mraible thanks for the report!

I have debugged this with the help of @marcusdacoregio and found that the CookieCsrfTokenRepository is overwriting the Set-Cookie header with b79ba89.

Since both the JSESSIONID and XSRF-TOKEN cookies are rotated on successful login, subsequent requests (after login) are not using the correct session id, which causes those requests to be unauthenticated (anonymous).

I'll update the title of this issue to be specific to the problem and add a fix. Thanks again!

@sjohnr sjohnr moved this from Prioritized to In Progress in Spring Security Team Apr 25, 2023
@sjohnr sjohnr changed the title CSRF behavior is different between Spring Boot 3.0.5 and 3.1.0 CookieCsrfTokenRepository overwrites previous Set-Cookie response headers Apr 25, 2023
@sjohnr sjohnr closed this as completed in 07b884a Apr 25, 2023
@github-project-automation github-project-automation bot moved this from In Progress to Done in Spring Security Team Apr 25, 2023
@sjohnr sjohnr added this to the 6.1.0 milestone Apr 25, 2023
@mraible
Copy link
Contributor Author

mraible commented Apr 26, 2023

@sjohnr I tried testing this with Spring Boot 3.1.0-SNAPSHOT today, but it still seems to pull in Spring Security 6.0.2. Is there an easy way to tell Maven to use Spring Security 6.1.0-SNAPSHOT?

Also, I saw the following deprecation errors when building.

[WARNING] /Users/mraible/.../SecurityConfiguration.java:[23,13] oauth2Login() in org.springframework.security.config.annotation.web.builders.HttpSecurity has been deprecated and marked for removal
[WARNING] /Users/mraible/.../SecurityConfiguration.java:[24,13] oauth2ResourceServer() in org.springframework.security.config.annotation.web.builders.HttpSecurity has been deprecated and marked for removal
[WARNING] /Users/mraible/.../SecurityConfiguration.java:[24,36] jwt() in org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer has been deprecated and marked for removal
[WARNING] /Users/mraible/.../SecurityConfiguration.java:[26,13] csrf() in org.springframework.security.config.annotation.web.builders.HttpSecurity has been deprecated and marked for removal

However, when I dig into the source code, I don't see the methods as deprecated.

@marcusdacoregio
Copy link
Contributor

marcusdacoregio commented Apr 26, 2023

Hi @mraible,

I think that Spring Boot snapshots are not using Spring Security snapshots, see https://github.com/spring-projects/spring-boot/blob/9a0b5e01789495b87c5a11bc9b9d6af5d92a8e08/spring-boot-project/spring-boot-dependencies/build.gradle#L1471.

but it still seems to pull in Spring Security 6.0.2.
Maybe you have to force refresh your dependencies?

You can override the Spring Security version by adding a property to your pom.xml file:

<properties>
  <spring-security.version>6.1.0-SNAPSHOT</spring-security.version>
</properties>

@mraible
Copy link
Contributor Author

mraible commented Apr 26, 2023

@marcusdacoregio Thank you! Adding the spring-security.version solves the problem.

I'm still curious about the deprecation warnings.

@marcusdacoregio
Copy link
Contributor

Sorry, I haven't noticed the line about the deprecations.

You can have more detail about the deprecations on this issue #12629 and in the 6.1.0-SNAPSHOT documentation.

@mraible
Copy link
Contributor Author

mraible commented Apr 26, 2023

Thanks @marcusdacoregio! I was able to solve the deprecation warnings by changing from this:

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http.authorizeHttpRequests((authz) -> authz
        .requestMatchers("/", "/index.html", "*.ico", "*.css", "*.js", "/api/user").permitAll()
        .anyRequest().authenticated()
    );

    http.oauth2Login();
    http.oauth2ResourceServer().jwt();

    http.csrf()
        .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
        .csrfTokenRequestHandler(new CsrfTokenRequestAttributeHandler());
    http.addFilterAfter(new CookieCsrfFilter(), BasicAuthenticationFilter.class);

    http.addFilterAfter(new SpaWebFilter(), BasicAuthenticationFilter.class);

    return http.build();
}

To this:

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http.authorizeHttpRequests((authz) -> authz
            .requestMatchers("/", "/index.html", "*.ico", "*.css", "*.js", "/api/user").permitAll()
            .anyRequest().authenticated())
        .oauth2Login(withDefaults())
        .oauth2ResourceServer((oauth2) -> oauth2.jwt(withDefaults()))
        .csrf((csrf) -> csrf
            .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
            .csrfTokenRequestHandler(new CsrfTokenRequestAttributeHandler()))
        .addFilterAfter(new CookieCsrfFilter(), BasicAuthenticationFilter.class)
        .addFilterAfter(new SpaWebFilter(), BasicAuthenticationFilter.class);

    return http.build();
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: web An issue in web modules (web, webmvc) type: bug A general bug
Projects
Archived in project
Development

No branches or pull requests

4 participants