Spring Security 3.2 CSRF支持多部分请求

时间:2014-01-28 05:53:34

标签: spring spring-mvc spring-security csrf

我们已经将Spring Security与我们的应用程序一起使用了几年。上周我们将Spring Security从3.1.4升级到3.2.0。升级很顺利,我们在升级后没有发现任何错误。

在查看Spring Security 3.2.0文档时,我们遇到了围绕CSRF保护和安全标头的新增功能。我们按照Spring Security 3.2.0文档中的说明为受保护资源启用CSRF保护。它适用于常规表单,但不适用于我们的应用程序中的多部分表单。在表单提交时,CsrfFilter会引发拒绝访问错误,因为请求中缺少CSRF令牌(通过DEBUG日志确定)。我们尝试使用Spring Security documentation中建议的第一个选项来使CSRF保护与多部分表单一起工作。我们不想使用第二个建议的选项,因为它通过URL泄漏CSRF令牌并带来安全风险。

基于文档的配置的相关部分在Github上以Gist的形式提供。我们使用的是Spring 4.0.0版。

请注意,我们已尝试以下变体但未成功:

  1. 未在MultipartFilter中声明web.xml
  2. 未在MultipartFilter中为web.xml设置解析程序bean名称。
  3. filterMultipartResolver中使用默认的解析程序bean名称webContext.xml
  4. 更新:我已确认即使使用单页示例应用,记录的行为也不起作用。任何人都可以确认记录的行为按预期工作吗?是否有可以使用的示例工作应用程序?

4 个答案:

答案 0 :(得分:44)

我能够在Spring Security团队的帮助下解决这个问题。我更新了Gist以反映工作配置。我必须按照下面给出的步骤,以使一切按预期工作。


<强> 1。共同步骤

the answer by @holmis83中所述,向MultipartFilter添加web.xml,确保在Spring Security配置之前添加<filter> <display-name>springMultipartFilter</display-name> <filter-name>springMultipartFilter</filter-name> <filter-class>org.springframework.web.multipart.support.MultipartFilter</filter-class> </filter> <filter-mapping> <filter-name>springMultipartFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter> <display-name>springSecurityFilterChain</display-name> <filter-name>springSecurityFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/*</url-pattern> <dispatcher>ERROR</dispatcher> <dispatcher>FORWARD</dispatcher> <dispatcher>REQUEST</dispatcher> </filter-mapping>

filterMultipartResolver

<强> 2.1。使用Apache Commons Multipart Resolver

确保在根Spring应用程序上下文中有一个名为<context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath*:springWebMultipartContext.xml </param-value> </context-param> 的Apache Commons Multipart Resolver bean 。我将再次强调这一点,确保在根Spring Context (通常称为 applicationContext.xml )中声明了Multipart Resolver。例如,

的web.xml

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="filterMultipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="maxUploadSize" value="100000000" />
    </bean>
</beans>

springWebMultipartContext.xml

MultipartFilter

确保bean名为 filterMultipartResolver ,因为web.xml中配置的MultipartFilter未选取任何其他bean名称。我的初始配置无效,因为此bean名为 multipartResolver 。我甚至尝试使用web.xml init-param将bean名称传递给context.xml,但这也不起作用。

<强> 2.2。使用Tomcat Multipart支持

Tomcat 7.0+具有内置的多部分支持,但必须明确启用它。要么按如下所示更改全局Tomcat context.xml文件,要么在WAR文件中包含本地<Context allowCasualMultipartParsing="true"> ... </Context> 文件,以使此支持无需对应用程序进行任何其他更改即可工作。

{{1}}

使用Apache Commons Multipart Resolver进行这些更改后,我们的应用程序目前在Tomcat,Jetty和Weblogic上运行。

答案 1 :(得分:4)

这部分:

<filter-mapping>
    <filter-name>multipartFilter</filter-name>
    <servlet-name>/*</servlet-name>
</filter-mapping>

应该是:

<filter-mapping>
    <filter-name>multipartFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

Spring Security 3.2.0文档中存在错误。错误has been reported将在即将发布的版本中修复。

答案 2 :(得分:3)

稍微讨论了这个问题之后,我发现了一个更简单的解决方案,就是使用Spring Security中定义的Request Header,而不是试图将CSRF令牌作为多部分内容的一部分嵌入。

这是我在jsp中使用AJAX库设置头文件的简单方法:

var uploader = new AjaxUpload({
        url: '/file/upload',
        name: 'uploadfile',
        multipart: true,
        customHeaders: { '${_csrf.headerName}': '${_csrf.token}' },
        ...
        onComplete: function(filename, response) {
            ...
        },
        onError: function( filename, type, status, response ) {
            ...
        }
});

反过来又发送带有标题的多部分请求:

X-CSRF-TOKEN: abcdef01-2345-6789-abcd-ef0123456789

他们在标题中嵌入<meta />标记的建议也可以正常工作,暂停提交请求,通过javascript添加标题,然后完成提交:

<html>
<head>
    <meta name="_csrf" content="${_csrf.token}"/>
    <!-- default header name is X-CSRF-TOKEN -->
    <meta name="_csrf_header" content="${_csrf.headerName}"/>
    <!-- ... -->
</head>
<body>
    <!-- ... -->
    <script>
        var token = $("meta[name='_csrf']").attr("content");
        var header = $("meta[name='_csrf_header']").attr("content");
        // Do whatever with values
    </script>
</body>
</html>

更多信息:Spring Security - CSRF for AJAX and JSON Requests

答案 3 :(得分:2)

找到最答案的答案是几年前的服务器。

如果需要

  

通过RestTemplate传递CSRF令牌

这个博客很有启发性https://cloudnative.tips/passing-csrf-tokens-with-resttemplate-736b336a6cf6

在Spring Security 5.0.7.RELEASE中

https://docs.spring.io/spring-security/site/docs/current/reference/html/csrf.html#csrf-multipart

  

使用CSRF保护有两种选择   多部分/表单数据。每个选项都有其权衡。

     

-在Spring Security之前放置MultipartFilter
  -在其中包含CSRF令牌   动作

简而言之,第一个选项更安全,后者更容易。

  在Spring Security过滤器之前

指定MultipartFilter   表示没有授权调用MultipartFilter   这意味着任何人都可以在您的服务器上放置临时文件。然而,   只有授权用户才能提交已处理的文件   根据您的应用程序。通常,这是推荐的方法   因为临时文件上传对   大多数服务器。

     

确保在Spring Security之前指定MultipartFilter   使用Java配置进行过滤,用户可以覆盖   beforeSpringSecurityFilterChain如下所示:

public class SecurityApplicationInitializer extends AbstractSecurityWebApplicationInitializer {

  @Override
  protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
      insertFilters(servletContext, new MultipartFilter());
  }
}
     

确保在Spring Security之前指定MultipartFilter   使用XML配置进行过滤,用户可以确保   MultipartFilter的元素放置在   web.xml中的springSecurityFilterChain如下所示:

<filter>
  <filter-name>MultipartFilter</filter-name>
  <filter-class>org.springframework.web.multipart.support.MultipartFilter</filter-class>
</filter>
<filter>
  <filter-name>springSecurityFilterChain</filter-name>
  <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
  <filter-name>MultipartFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
  <filter-name>springSecurityFilterChain</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

另一个选项

  

如果不允许未经授权的用户上传临时文件,则不会   可以接受的一种替代方法是将MultipartFilter放在   Spring Security过滤器,并将CSRF作为查询参数包含在   表单的动作属性。带有jsp的示例如下所示

<form action="./upload?${_csrf.parameterName}=${_csrf.token}" method="post" enctype="multipart/form-data">
     

此方法的缺点是查询参数可以是   泄漏了。更一般地说,放置   正文或标头中包含敏感数据,以确保其不会泄漏。