tutorial example spring-mvc spring-security spring-saml

spring mvc - example - Spring SAML Extension y Spring Security CSRF Conflict Protection



spring security saml (3)

Tenemos una aplicación Spring MVC (4.0.5) con Spring Security (3.2.4) que incluye protección CSRF que funciona bien. Ahora estamos agregando la extensión de seguridad SAML (spring-security-saml2-core 1.0.0) que causa un problema con la protección CSRF.

Los metadatos se han configurado en SSOCircle e intentan acceder a http://localhost:8080/myapp dirige a la página de inicio de sesión en SSOCircle. Después de la autenticación, el navegador redirige a http://localhost:8080/myapp/saml/SSO y genera un error:

Estado de HTTP 403: token de CSRF esperado no encontrado. Ha expirado su sesión?

Si desactivamos la protección CSRF, todo funciona. ¿Cómo podemos mantener la protección CSRF y aún usar la extensión SAML?

Antes de configurar la extensión SAML, utilizamos un formulario de inicio de sesión y la protección CSRF funcionó y no recibimos un error en el inicio de sesión JSP y no tenía el token.

Código antes de 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"); }

Código con 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("/"); }

ACTUALIZAR

Después de volver a habilitar la protección CSRF y configurar el registro en DEPURAR, aquí están los registros que se producen justo después de la autenticación exitosa:

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


Tienes al menos dos opciones.

Una es implementar un RequestMatcher personalizado ( org.springframework.security.web.util.RequestMatcher ) que no coincidirá con las URL Spring SAML y proporcionar esto a la configuración de csrf con:

http.csrf().requireCsrfProtectionMatcher(matcher);

El otro, más fácil es definir los puntos finales de Spring SAML en una configuración de http separada que no tendrá habilitada la protección csrf.

La configuración XML para hacer esto puede ser similar a:

<!-- 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>

Para la configuración de Java algo como esto debería funcionar:

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

Los detalles sobre la definición de la configuración http múltiple con la configuración de Java se pueden encontrar en el manual de Spring Security .


Para otros que podrían enfrentar este problema, también pude resolverlo forzando el orden de los filtros.

Reemplacé esto:

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

con este:

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

y ahora funciona.