Spring SAML Extension and Spring Security CSRF Protection Conflict

眉间皱痕 提交于 2019-12-21 04:08:47

问题


We have a Spring MVC (4.0.5) application with Spring Security (3.2.4) which includes CSRF protection which works fine. We are now adding the SAML security extension (spring-security-saml2-core 1.0.0) which causes an issue with CSRF protection.

The metadata has been configured on SSOCircle and trying to access http://localhost:8080/myapp directs to the login page on SSOCircle. After authentication, the browser redirects to http://localhost:8080/myapp/saml/SSO and generates an error:

HTTP Status 403 - Expected CSRF token not found. Has your session expired?

If we turn off CSRF protection, everything works. How can we maintain CSRF protection and still use the SAML extension?

Before setting up the SAML extension, we used a login form and CSRF protection worked and we did not receive an error on the login JSP and it did not have the token.

Code before SAML:

@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
    httpSecurity.authorizeRequests()
            .antMatchers("/login", "/login.request", "/logout").permitAll()
            .anyRequest()
                .hasAnyAuthority("MyRole")
                    .and().formLogin()
            .loginPage("/login.request").loginProcessingUrl("/login")
            .failureUrl("/login.request?error").permitAll().and().logout()
            .logoutUrl("/logout").permitAll()
            .logoutSuccessUrl("/login.request");
}

Code with SAML:

@Override
protected void configure(HttpSecurity http) throws Exception {
    //http.csrf().disable();

    http.httpBasic().authenticationEntryPoint(samlEntryPoint());

    http.addFilterBefore(metadataGeneratorFilter(),
            ChannelProcessingFilter.class).addFilterAfter(samlFilter(),
            BasicAuthenticationFilter.class);

    http
        .authorizeRequests()
            .antMatchers("/error").permitAll()
            .antMatchers("/saml/**").permitAll()
            .anyRequest()
                .hasAnyAuthority("MyRole")
            .anyRequest().authenticated();

    http.logout().logoutSuccessUrl("/");
}

UPDATE

After re-enabling CSRF protection and setting logging to DEBUG, here are the logs that occur right after the successful authentication:

22.10.2014 16:54:17.374 [http-bio-8080-exec-8] DEBUG o.s.w.m.support.MultipartFilter -
                Using MultipartResolver 'filterMultipartResolver' for MultipartFilter

22.10.2014 16:54:17.377 [http-bio-8080-exec-8] DEBUG o.s.b.f.s.DefaultListableBeanFactory -
                Returning cached instance of singleton bean 'filterMultipartResolver'

22.10.2014 16:54:17.788 [http-bio-8080-exec-8] DEBUG o.s.w.m.support.MultipartFilter -
                Request [/epass/saml/SSO] is not a multipart request

22.10.2014 16:54:17.790 [http-bio-8080-exec-8] DEBUG o.s.s.w.u.m.AntPathRequestMatcher -
                Checking match of request : '/saml/sso'; against '/resources/**'

22.10.2014 16:54:17.791 [http-bio-8080-exec-8] DEBUG o.s.security.web.FilterChainProxy -
                /saml/SSO at position 1 of 14 in additional filter chain; firing Filter: 'MetadataGeneratorFilter'

22.10.2014 16:54:17.793 [http-bio-8080-exec-8] DEBUG o.s.security.web.FilterChainProxy -
                /saml/SSO at position 2 of 14 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'

22.10.2014 16:54:17.795 [http-bio-8080-exec-8] DEBUG o.s.security.web.FilterChainProxy -
                /saml/SSO at position 3 of 14 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'

22.10.2014 16:54:17.797 [http-bio-8080-exec-8] DEBUG o.s.s.w.c.HttpSessionSecurityContextRepository -
                HttpSession returned null object for SPRING_SECURITY_CONTEXT

22.10.2014 16:54:17.798 [http-bio-8080-exec-8] DEBUG o.s.s.w.c.HttpSessionSecurityContextRepository -
                No SecurityContext was available from the HttpSession: org.apache.catalina.session.StandardSessionFacade@b08c9c9. A new one will be created.

22.10.2014 16:54:17.800 [http-bio-8080-exec-8] DEBUG o.s.security.web.FilterChainProxy -
                /saml/SSO at position 4 of 14 in additional filter chain; firing Filter: 'HeaderWriterFilter'

22.10.2014 16:54:17.801 [http-bio-8080-exec-8] DEBUG o.s.s.w.h.writers.HstsHeaderWriter -
                Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@244a79ef

22.10.2014 16:54:17.802 [http-bio-8080-exec-8] DEBUG o.s.security.web.FilterChainProxy -
                /saml/SSO at position 5 of 14 in additional filter chain; firing Filter: 'CsrfFilter'
22.10.2014 16:54:17.805 [http-bio-8080-exec-8] DEBUG o.s.security.web.csrf.CsrfFilter -
                Invalid CSRF token found for `http://localhost:8080/myapp/saml/SSO`

22.10.2014 16:54:17.807 [http-bio-8080-exec-8] DEBUG o.s.s.w.c.HttpSessionSecurityContextRepository -
                SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.

22.10.2014 16:54:17.808 [http-bio-8080-exec-8] DEBUG o.s.s.w.c.SecurityContextPersistenceFilter -
                SecurityContextHolder now cleared, as request processing completed

回答1:


You have at least two options.

One is to implement a custom RequestMatcher (org.springframework.security.web.util.RequestMatcher) which will not match on Spring SAML URLs and provide this to the csrf configuration with:

http.csrf().requireCsrfProtectionMatcher(matcher);

The other, easier is to define Spring SAML endpoints in a separate http configuration which will not have the csrf protection enabled.

The XML configuration to do this can be similar to:

<!-- SAML processing endpoints -->
<security:http pattern="/saml/**" entry-point-ref="samlEntryPoint">
    <security:custom-filter before="FIRST" ref="metadataGeneratorFilter"/>
    <security:custom-filter after="BASIC_AUTH_FILTER" ref="samlFilter"/>
</security:http>

<!-- Secured pages with SAML as entry point -->
<security:http entry-point-ref="samlEntryPoint">
    <security:csrf />
    <security:intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY"/>
    <security:custom-filter before="FIRST" ref="metadataGeneratorFilter"/>
</security:http>

For Java configuration something like this should work:

@Configuration
@EnableWebSecurity
public class MutlipleHttpConfigurationConfig {

    @Configuration
    @Order(1)
    public static class SAMLWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
        protected void configure(HttpSecurity http) throws Exception {
            http.antMatcher("/saml/**");
            http.csrf().disable();
            http.httpBasic().authenticationEntryPoint(samlEntryPoint());
            http.addFilterBefore(metadataGeneratorFilter(),
                    ChannelProcessingFilter.class).addFilterAfter(samlFilter(),
                    BasicAuthenticationFilter.class);
        }
    }

    @Configuration
    public static class BasicWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
        protected void configure(HttpSecurity http) throws Exception {
            http.httpBasic().authenticationEntryPoint(samlEntryPoint());
            http.addFilterBefore(metadataGeneratorFilter(), ChannelProcessingFilter.class);
            http
                    .authorizeRequests()
                    .antMatchers("/error").permitAll()
                    .anyRequest()
                    .hasAnyAuthority("MyRole")
                    .anyRequest().authenticated();

            http.logout().logoutSuccessUrl("/");
        }
    }
}

Details on defining multiple http configuration with Java configuration can be found in the Spring Security manual.




回答2:


This works for me:

http.csrf().ignoringAntMatchers("/saml/**") ...

So CSRF is disabled when processing /saml/**

from : https://github.com/choonchernlim/spring-security-adfs-saml2/blob/master/src/main/java/com/github/choonchernlim/security/adfs/saml2/SAMLWebSecurityConfigurerAdapter.java




回答3:


For others who might face this issue, I was also able to solve this by forcing the order of the filters.

I replaced this:

http.addFilterBefore(metadataGeneratorFilter(), ChannelProcessingFilter.class)
    .addFilterAfter(samlFilter(), BasicAuthenticationFilter.class);

with this:

FilterChainProxy samlFilter = samlFilter();

http.addFilterBefore(metadataGeneratorFilter(), ChannelProcessingFilter.class)
    .addFilterAfter(samlFilter, BasicAuthenticationFilter.class)
    .addFilterBefore(samlFilter, CsrfFilter.class);

and it now works.



来源:https://stackoverflow.com/questions/26508835/spring-saml-extension-and-spring-security-csrf-protection-conflict

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!