Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.
like image 830
user1459144 Avatar asked Oct 01 '14 21:10

user1459144


3 Answers

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.

like image 74
M. Deinum Avatar answered Nov 19 '22 16:11

M. Deinum


Basically the main complexity was in the overriding logoutFilter in the Spring Security XML context to work with default implementation of org.springframework.security.web.util.matcher.AntPathRequestMatcher (that works with "GET" not "POST" requests). In order to do this several beans were added to security xml context:

 <bean id="logoutAntPathRequestMatcher" class="org.springframework.security.web.util.matcher.AntPathRequestMatcher">
        <constructor-arg value="logout" />
    </bean>

and logout filter itself:

<bean id="logoutFilter"
    class="org.springframework.security.web.authentication.logout.LogoutFilter">
    <constructor-arg  name="logoutSuccessHandler" ref="logoutSuccessHandler"/>
    <constructor-arg  name="handlers">
        <list>
            <ref bean="securityContextLogoutHandler" />
            <ref bean="cookieClearingLogoutHandler" />
            <ref bean="csrfLogoutHandler" />
        </list>
    </constructor-arg>
    <property name="filterProcessesUrl" value="/logout"/>
    <property name="logoutRequestMatcher" ref="logoutAntPathRequestMatcher"/>
</bean>
like image 4
user1459144 Avatar answered Nov 19 '22 16:11

user1459144


I saw the same error after Internet Explorer 11 update. CsrfConfigurer.class is not null and post is expected when logging out.

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

I solved my problem by bypassing the logoutfilter and insert new filter to spring security

Example is below.

    <beans:bean id="logoutAntPathRequestMatcher" class="org.springframework.security.web.util.matcher.AntPathRequestMatcher">
            <beans:constructor-arg value="/logout"/>
        </beans:bean>

        <beans:bean id="securityContextLogoutHandler" class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler">

        </beans:bean>


        <beans:bean id="cookieClearingLogoutHandler" class="org.springframework.security.web.authentication.logout.CookieClearingLogoutHandler">
            <beans:constructor-arg value="JSESSIONID"/>
        </beans:bean>

        <beans:bean id="logoutFilter"
              class="org.springframework.security.web.authentication.logout.LogoutFilter">
            <beans:constructor-arg  name="logoutSuccessUrl" value="/login"/>
            <beans:constructor-arg  name="handlers">
                <beans:list>
                    <beans:ref bean="securityContextLogoutHandler" />
                    <beans:ref bean="cookieClearingLogoutHandler" />
                </beans:list>
            </beans:constructor-arg>
            <beans:property name="filterProcessesUrl" value="/logout"/>
            <beans:property name="logoutRequestMatcher" ref="logoutAntPathRequestMatcher"/>
        </beans:bean>


<http>
...
<sec:custom-filter ref="logoutFilter" after="LOGOUT_FILTER"/>
...
</http>
like image 1
Gurkan İlleez Avatar answered Nov 19 '22 17:11

Gurkan İlleez