Spring CSRF 覆盖安全 XML 配置中的“POST”注销行为

Posted

技术标签:

【中文标题】Spring CSRF 覆盖安全 XML 配置中的“POST”注销行为【英文标题】:Spring CSRF override "POST" logout behaviour in security XML config 【发布时间】:2014-11-26 20:50:43 【问题描述】:

目前我们的遗留应用程序的 Spring CSRF 解决方案存在问题,因为 CSRF 实现改变了默认 Spring 安全性的行为 Spring 安全配置如下:

<http pattern="">
...
<logout
                logout-url="/logout"
                delete-cookies="..."
                success-handler-ref="logoutSuccessHandler"
                />
<csrf/>
</http>

org.springframework.security.config.annotation.web.configurers.LogoutConfigurer 注销配置器。 根据 Spring 文档:

添加 CSRF 将更新 LogoutFilter 以仅使用 HTTP POST。这 确保注销需要 CSRF 令牌并且恶意用户 无法强制注销您的用户。

进行此更改的代码如下:

 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;
    

一般来说,对于 CSRF 保护,这种行为是完全合理的。但对我来说,这个实现不灵活很奇怪(为什么要硬编码真正的实现而不是自动装配依赖?)。

问题是我们的应用程序是这样构建的,在常规的 Spring 注销之前,它在 Spring 控制器中执行了额外的清理。主要是实现Switch Userfeature,但以自定义方式。因此,更改注销链接以执行 POST 不是一种选择,因为主要是在自定义控制器上执行清理。

似乎为了使用指定的方法,只有一种可能的解决方案:

@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

//如果决定注销,这将转到这个Controller方法:

  @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
     

从代码质量的角度来看,这种方法通常不好。 所以,有两个问题:

    是什么原因在 Spring 中进行如此严格的实现并且不提供任何可见的覆盖它的可能性(特别是我提供了它是如何创建的代码示例)? 解决上述问题的任何好方法。

【问题讨论】:

你为什么首先使用控制器?一般来说,最好将这种逻辑放在LogoutHandler 中,这样您就可以很好地与 Spring Security 集成,而不是尝试在控制器中解决它。如果您在清理后转发(而不是重定向!)到注销 URL,POST 仍然可以工作。 此外,没有什么可以阻止您将注销配置为处理 GET 而不管 CSFR 保护如何,如果您覆盖它,这只是默认设置。 您能否添加更多详细信息,如何配置注销以在支持 CSRF 的 GET 上处理(仅作为本帖的答案发布)? @user1459144 在这里查看蓝图:gist.github.com/marcelstoer/c9a894a929c8fe38222a 【参考方案1】:

您描述的行为是如果您没有明确配置注销支持而仅启用它的行为,如果您明确配置它,它将使用该配置。

@Override
protected void configure(HttpSecurity http) throws Exception 
    http
        .logout()
            .logoutRequestMatcher(new AntPathRequestMatcher("/logout"));

这也是参考指南中的documented。

然而,真正的解决方案是恕我直言,您不应该使用控制器来实现额外的注销功能,而是使用LogoutHandler。这将与 Spring Security 很好地集成,您不需要重定向/转发到不同的 URL。

【讨论】:

你知道在 XML 配置中实现它的简单方法吗? 您发布的代码与基于 java 的配置而不是 xml 有关。在 XML 中,csfr 默认为禁用且未启用。 LogoutConfigurer 仅在您使用基于 java 的配置而不用于 XML 配置时使用。所以我想说你的问题令人困惑。对于 XML,恐怕您必须手动配置 LogoutFilter,而不是使用命名空间作为 LogoutBeanDefinitionParser(已使用!)在启用 csfr 时总是转向 POST。 对不起,我没有指出使用 Spring 安全 XML 配置。 LogoutConfigurer 也用于 XML 配置。我将研究手动配置注销过滤器的方法 不,不是……LogoutBeanDefinitionParser 不是LogoutConfigurer。这就是为什么它在 annotation 子包中,基于 xml 的子包在 config.http 包中。 是的,手动配置注销过滤器的方法工作正常。谢谢建议【参考方案2】:

基本上,主要的复杂性在于 Spring Security XML 上下文中覆盖 logoutFilter 以与 org.springframework.security.web.util.matcher.AntPathRequestMatcher 的默认实现一起使用(与“GET”而不是“POST”请求一起使用) . 为了做到这一点,将几个 bean 添加到安全 xml 上下文中:

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

和注销过滤器本身:

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

【讨论】:

【参考方案3】:

在 Internet Explorer 11 更新后我看到了同样的错误。 CsrfConfigurer.class 不为空,注销时需要发布。

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

我通过绕过注销过滤器并将新过滤器插入弹簧安全解决了我的问题

示例如下。

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

【讨论】:

实际上这是 XML 配置唯一可行的解​​决方案。 logoutAntPathRequestMatcher bean 和对它的引用可以省略,因为 filterProcessesUrl 属性设置器在后台实例化完全相同的匹配器。

以上是关于Spring CSRF 覆盖安全 XML 配置中的“POST”注销行为的主要内容,如果未能解决你的问题,请参考以下文章

Spring Security CSRF 支持手动安全配置

如何通过 XML 配置仅针对特定 URL 模式在 Spring Security 4 中禁用 CSRF?

重命名 Spring csrf 令牌变量

spring security4.2 配置CSRF防御场景

Spring 安全中的条件 CSRF 保护

Spring 安全配置无法正常工作