Spring SAML Extension and Spring CSRF Protection Conflict Protection

We have a Spring MVC application (4.0.5) with Spring Security (3.2.4) that includes CSRF protection, which works fine. Now we are adding the SAML security extension (spring-security-saml2-core 1.0.0), which causes a CSRF protection problem.

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

HTTP Status 403 - The expected CSRF token was not found. Session expired?

If we disable CSRF protection, everything will work. How can we maintain CSRF protection and still use the SAML extension?

Before setting up the SAML extension, we used the login form, and the CSRF protection worked, and we did not receive an error in the JSP login, and it did not have a 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 registering the settings in DEBUG, here are the logs that occur immediately after successful authentication:

 22.10.2014 16:54:17.374 [http-bio-8080-exec-8] DEBUG oswmsupport.MultipartFilter - Using MultipartResolver 'filterMultipartResolver' for MultipartFilter 22.10.2014 16:54:17.377 [http-bio-8080-exec-8] DEBUG osbfsDefaultListableBeanFactory - Returning cached instance of singleton bean 'filterMultipartResolver' 22.10.2014 16:54:17.788 [http-bio-8080-exec-8] DEBUG oswmsupport.MultipartFilter - Request [/epass/saml/SSO] is not a multipart request 22.10.2014 16:54:17.790 [http-bio-8080-exec-8] DEBUG osswumAntPathRequestMatcher - Checking match of request : '/saml/sso'; against '/resources/**' 22.10.2014 16:54:17.791 [http-bio-8080-exec-8] DEBUG ossecurity.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 ossecurity.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 ossecurity.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 osswcHttpSessionSecurityContextRepository - HttpSession returned null object for SPRING_SECURITY_CONTEXT 22.10.2014 16:54:17.798 [http-bio-8080-exec-8] DEBUG osswcHttpSessionSecurityContextRepository - 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 ossecurity.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 osswhwriters.HstsHeaderWriter - Not injecting HSTS header since it did not match the requestMatcher org.springframework.se curity.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@ 244a79ef 22.10.2014 16:54:17.802 [http-bio-8080-exec-8] DEBUG ossecurity.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 ossecurity.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 osswcHttpSessionSecurityContextRepository - 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 osswcSecurityContextPersistenceFilter - SecurityContextHolder now cleared, as request processing completed 
+5
source share
3 answers

You have at least two options.

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

 http.csrf().requireCsrfProtectionMatcher(matcher); 

It is easier for others to define Spring SAML endpoints in a separate http configuration in which csrf protection will not be enabled.

The XML configuration for this may look like:

 <!-- 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 a 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("/"); } } } 

For information on configuring multiple http with Java configuration, see the Spring Security Guide .

+9
source

For others who might run into this problem, 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 now it works.

+4
source

This works for me:

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

Therefore, 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
source

Source: https://habr.com/ru/post/1205272/


All Articles