我们已经将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版。
请注意,我们已尝试以下变体但未成功:
MultipartFilter
中声明web.xml
。MultipartFilter
中为web.xml
设置解析程序bean名称。filterMultipartResolver
中使用默认的解析程序bean名称webContext.xml
。更新:我已确认即使使用单页示例应用,记录的行为也不起作用。任何人都可以确认记录的行为按预期工作吗?是否有可以使用的示例工作应用程序?
答案 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>
答案 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">
此方法的缺点是查询参数可以是 泄漏了。更一般地说,放置 正文或标头中包含敏感数据,以确保其不会泄漏。