Spring Security 3.2 CSRF支持多部分请求

我们已经使用Spring Security与我们的应用程序几年了。 上个星期我们把Spring Security从版本3.1.4升级到3.2.0。 升级进展良好,我们没有发现升级后的任何错误。

在浏览Spring Security 3.2.0文档时,我们遇到了有关CSRF保护和安全头的新增function。 我们按照Spring Security 3.2.0文档中的说明为我们的受保护资源启用CSRF保护。 它对于常规表单工作正常,但在我们的应用程序中不适用于多部分表单。 在表单提交时, CsrfFilter引发请求中缺lessCSRF标记(通过DEBUG日志确定)的“拒绝访问”错误。 我们已经尝试过使用Spring Security文档中提出的第一个选项来使用多部分表单进行CSRF保护。 我们不希望使用第二个build议的选项,因为它通过URL泄露CSRF令牌,并带来安全风险。

基于文档的configuration的相关部分在Github上作为Gist可用。 我们正在使用Spring版本4.0.0。

请注意,我们已经尝试了以下变化而没有成功:

  1. 不在web.xml声明MultipartFilter
  2. 不要在web.xml设置MultipartFilter的parsing器bean名称。
  3. webContext.xml使用默认的parsing器bean名称webContext.xml

更新:我已经确认即使使用单个页面示例应用程序,logging的行为也不起作用。 任何人都可以确认logging的行为是否按预期工作? 有没有可以使用的示例工作应用程序?

我能够在Spring Security团队的帮助下解决这个问题。 我已经更新了Gist以反映工作configuration。 我不得不按照下面给出的步骤来使一切按预期工作。


1.共同步骤

在上面的答案中添加一个MultipartFilterweb.xml ,如上面的答案中的@ holmis83所描述的,确保在Spring Securityconfiguration之前添加:

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

2.1。 使用Apache Commons Multipartparsing器

确保在根Spring应用程序上下文中有一个名为filterMultipartResolver的Apache Commons Multipart Resolver bean。 我将再次强调这一点, 确保Multipart Resolver是在根Spring Context (通常称为applicationContext.xml )中声明的。 例如,

web.xml中

 <context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath*:springWebMultipartContext.xml </param-value> </context-param> 

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

确保bean被称为filterMultipartResolver,因为任何其他bean名称都不是由web.xmlconfiguration的MultipartFilter拾取的。 我的初始configuration不起作用,因为这个bean被命名为multipartResolver 。 我甚至尝试使用web.xml init-param将bean名称传递给MultipartFilter但是这也不起作用。

2.2。 使用Tomcat Multipart支持

Tomcat 7.0+具有内置多部分支持,但必须明确启用。 请按如下所示更改全局Tomcat context.xml文件,或者在WAR文件中包含本地context.xml文件,以使此支持无需对应用程序进行任何其他更改即可使用。

 <Context allowCasualMultipartParsing="true"> ... </Context> 

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

这部分:

 <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文档中的一个错误。 该错误已被报告 ,并将在即将到来的版本中修复。

在解决了这个问题之后,我发现了一个简单的解决scheme,只需使用Spring Security中定义的请求头,而不是试图将CSRF令牌embedded为多部分内容的一部分。

这里有一个简单的方法,我使用AJAX库在jsp中设置file upload的头文件:

 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 

他们的build议embedded到标题中的<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 – 用于AJAX和JSON请求的CSRF