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。
请注意,我们已经尝试了以下变化而没有成功:
- 不在
web.xml
声明MultipartFilter
。 - 不要在
web.xml
设置MultipartFilter
的parsing器bean名称。 - 在
webContext.xml
使用默认的parsing器bean名称webContext.xml
。
更新:我已经确认即使使用单个页面示例应用程序,logging的行为也不起作用。 任何人都可以确认logging的行为是否按预期工作? 有没有可以使用的示例工作应用程序?
我能够在Spring Security团队的帮助下解决这个问题。 我已经更新了Gist以反映工作configuration。 我不得不按照下面给出的步骤来使一切按预期工作。
1.共同步骤
在上面的答案中添加一个MultipartFilter
到web.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.xml
configuration的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