Search code examples
javaspringspring-securitycsrf

Spring CSRF override "POST" logout behaviour in security XML config


Currently we have a problem with Spring CSRF solution for our legacy App because CSRF implementation changes behavior of default Spring security Spring security configuration sis following:

<http pattern="">
...
<logout
                logout-url="/logout"
                delete-cookies="..."
                success-handler-ref="logoutSuccessHandler"
                />
<csrf/>
</http>

org.springframework.security.config.annotation.web.configurers.LogoutConfigurer Logout configurer. According to Spring documentation:

Adding CSRF will update the LogoutFilter to only use HTTP POST. This ensures that log out requires a CSRF token and that a malicious user cannot forcibly log out your users.

Code that makes this change is the following:

 private RequestMatcher getLogoutRequestMatcher(H http) {
        if(logoutRequestMatcher != null) {
            return logoutRequestMatcher;
        }
        if(http.getConfigurer(CsrfConfigurer.class) != null) {
            this.logoutRequestMatcher = new AntPathRequestMatcher(this.logoutUrl, "POST");
        } else {
            this.logoutRequestMatcher = new AntPathRequestMatcher(this.logoutUrl);
        }
        return this.logoutRequestMatcher;
    }

Generally for CSRF protection such behavior makes perfect sense. But as for me it's very strange that this implementation is not flexible (why hardcode real implementations and not autowire dependencies?).

The problem is that our application is built in such way that before regular Spring logout it's performed additional clean up in Spring Controllers. Mainly it was implemented Switch Userfeature but in a custom way. So, changing logout link to perform POST is not an option because mainly clean up is performed on custom Controller.

It seems in order to use specified approach there is only one possible solution:

@RequestMapping(value = "/logout", method = RequestMethod.GET) //or it can be a post
    public String logout() {
// 1. Perform Clean up
// 2. Decide whether to logout or redirect to other page
// 3. Perform redirect based on decision
}

//If it's decided to logout this will go to this Controller method:

  @RequestMapping(value = "csrflogout", method = RequestMethod.GET)
    public void csrfLogout(){
//1 Create manual post request
//2. Copy session information
//3. Perform Post to logout URL that is specified in security xml
     }

Generally this approach is not good from code quality perspective. So, there are two questions:

  1. What is the reason to make such strict implementation in Spring and don't provide any visible possibility to override it (specifically I provided code example how it's created )?
  2. Any good alternative to fix mentioned problem.

Solution

  • The behavior you describe is the behavior if you don't explicitly configure logout support but only enable it, it you explicitly configure it it will use that configuration instead.

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .logout()
                .logoutRequestMatcher(new AntPathRequestMatcher("/logout"));
    }
    

    This is also documented in the reference guide.

    However the real solution is imho that you shouldn't use a controller for the additional logout functionality but use a LogoutHandler instead. That will integrate nicely with Spring Security and you don't need to redirect/forward to different URLs.