【问题标题】:Spring SAML Extension and Spring Security CSRF Protection ConflictSpring SAML 扩展和 Spring Security CSRF 保护冲突
【发布时间】:2014-12-17 23:37:46
【问题描述】:

我们有一个带有 Spring Security (3.2.4) 的 Spring MVC (4.0.5) 应用程序,其中包括可以正常工作的 CSRF 保护。我们现在添加了 SAML 安全扩展 (spring-security-saml2-core 1.0.0),这会导致 CSRF 保护出现问题。

元数据已在 SSOCircle 上配置,并尝试访问 http://localhost:8080/myapp 指向 SSOCircle 上的登录页面。鉴权后,浏览器重定向到http://localhost:8080/myapp/saml/SSO,报错:

HTTP 状态 403 - 未找到预期的 CSRF 令牌。您的会话是否已过期?

如果我们关闭 CSRF 保护,一切正常。我们如何在维护 CSRF 保护的同时仍然使用 SAML 扩展?

在设置 SAML 扩展之前,我们使用了登录表单并且 CSRF 保护工作,我们在登录 JSP 上没有收到错误并且它没有令牌。

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

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

更新

重新启用 CSRF 保护并将日志记录设置为 DEBUG 后,以下是成功验证后立即出现的日志:

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

【问题讨论】:

    标签: spring-mvc spring-security spring-saml


    【解决方案1】:

    通过将 添加到以下配置中,我能够通过 csrf 错误

    <!-- Secured pages with SAML as entry point -->
        <security:http entry-point-ref="samlEntryPoint" use-expressions="false">
            <security:csrf disabled="true"/>
            <security:intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY" />
            <security:custom-filter before="FIRST"  ref="metadataGeneratorFilter" />
            <security:custom-filter after="BASIC_AUTH_FILTER" ref="samlFilter" />
        </security:http>
    

    【讨论】:

      【解决方案2】:

      这对我有用:

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

      所以处理/saml/**时CSRF被禁用

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

      【讨论】:

        【解决方案3】:

        对于可能遇到此问题的其他人,我也可以通过强制过滤器的顺序来解决此问题。

        我替换了这个:

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

        用这个:

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

        它现在可以工作了。

        【讨论】:

        • 你拯救了我的一天!谢谢。
        【解决方案4】:

        您至少有两个选择。

        一种是实现一个自定义的RequestMatcher (org.springframework.security.web.util.RequestMatcher),它不会在 Spring SAML URL 上匹配,并将其提供给 csrf 配置:

        http.csrf().requireCsrfProtectionMatcher(matcher);
        

        另一个更简单的方法是在单独的 http 配置中定义 Spring SAML 端点,该配置不会启用 csrf 保护。

        执行此操作的 XML 配置可能类似于:

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

        对于 Java 配置,这样的东西应该可以工作:

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

        使用 Java 配置定义多个 http 配置的详细信息可以在Spring Security manual 中找到。

        【讨论】:

          猜你喜欢
          • 2020-05-31
          • 2015-07-30
          • 2016-01-12
          • 2015-08-27
          • 2014-10-14
          • 1970-01-01
          • 2016-09-01
          • 2013-03-02
          相关资源
          最近更新 更多