对于最新的稳定版本,请使用 Spring Security 6.5.3! |
Servlet 环境的跨站点请求伪造 (CSRF)
本节讨论 Spring Security 对 servlet 环境的跨站点请求伪造 (CSRF) 支持。
使用 Spring Security CSRF 保护
下面概述了使用 Spring Security 的 CSRF 保护的步骤:
使用正确的 HTTP 动词
防范 CSRF 攻击的第一步是确保您的网站使用正确的 HTTP 动词。安全方法必须是幂等的 中详细介绍了这一点。
配置 CSRF 保护
下一步是在应用程序中配置 Spring Security 的 CSRF 保护。默认情况下,Spring Security 的 CSRF 保护处于启用状态,但您可能需要自定义配置。以下是一些常见的自定义。
自定义 CsrfTokenRepository
默认情况下,Spring Security 将预期的 CSRF Tokens存储在HttpSession
用HttpSessionCsrfTokenRepository
. 在某些情况下,用户可能希望配置自定义CsrfTokenRepository
. 例如,可能需要保留CsrfToken
在 cookie 中支持基于 JavaScript 的应用程序。
默认情况下,CookieCsrfTokenRepository
将写入名为XSRF-TOKEN
并从名为X-XSRF-TOKEN
或 HTTP 参数_csrf
. 这些默认值来自 AngularJS
您可以配置CookieCsrfTokenRepository
在 XML 中使用以下命令:
<http>
<!-- ... -->
<csrf token-repository-ref="tokenRepository"/>
</http>
<b:bean id="tokenRepository"
class="org.springframework.security.web.csrf.CookieCsrfTokenRepository"
p:cookieHttpOnly="false"/>
示例显式设置 |
您可以配置CookieCsrfTokenRepository
在 Java 配置中使用:
-
Java
-
Kotlin
@EnableWebSecurity
public class WebSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
);
return http.build();
}
}
@EnableWebSecurity
class SecurityConfig {
@Bean
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
http {
csrf {
csrfTokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse()
}
}
return http.build()
}
}
示例显式设置 |
禁用 CSRF 保护
默认情况下启用 CSRF 保护。但是,如果对您的应用程序有意义,则禁用 CSRF 保护很简单。
下面的 XML 配置将禁用 CSRF 保护。
<http>
<!-- ... -->
<csrf disabled="true"/>
</http>
下面的 Java 配置将禁用 CSRF 保护。
-
Java
-
Kotlin
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable());
return http.build();
}
}
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
http {
csrf {
disable()
}
}
return http.build()
}
}
包括 CSRF Tokens
为了使同步器Tokens模式能够防止 CSRF 攻击,我们必须在 HTTP 请求中包含实际的 CSRF Tokens。这必须包含在浏览器不会自动包含在 HTTP 请求中的请求的一部分(即表单参数、HTTP 标头等)中。
Spring Security 的 CsrfFilter 将 CsrfToken 公开为HttpServletRequest
名为_csrf
. 这意味着任何视图技术都可以访问CsrfToken
将预期的Tokens公开为表单或元标记。幸运的是,下面列出了一些集成,使将Tokens包含在表单和 ajax 请求中变得更加容易。
表单 URL 编码
为了发布 HTML 表单,必须将 CSRF Tokens作为隐藏输入包含在表单中。例如,呈现的 HTML 可能如下所示:
<input type="hidden"
name="_csrf"
value="4bfd1575-3ad1-4d21-96c7-4ef2d9f86721"/>
接下来,我们将讨论将 CSRF Tokens包含在表单中作为隐藏输入的各种方法。
自动包含 CSRF Tokens
Spring Security 的 CSRF 支持通过其 CsrfRequestDataValueProcessor 提供与 Spring 的 RequestDataValueProcessor 的集成。这意味着,如果您利用 Spring 的表单标签库、Thymeleaf 或任何其他与RequestDataValueProcessor
,则具有不安全 HTTP 方法(即 post)的表单将自动包含实际的 CSRF Tokens。
csrfInput 标签
如果您使用的是 JSP,那么您可以使用 Spring 的表单标签库。但是,如果这不是一个选项,您也可以轻松地将Tokens包含在 csrfInput 标签中。
Ajax 和 JSON 请求
如果您使用的是 JSON,则无法在 HTTP 参数中提交 CSRF Tokens。 相反,您可以在 HTTP 标头中提交Tokens。
在以下部分中,我们将讨论在基于 JavaScript 的应用程序中将 CSRF Tokens作为 HTTP 请求标头包含的各种方法。
元标记
在 cookie 中公开 CSRF 的另一种模式是在meta
标签。
HTML 可能如下所示:
<html>
<head>
<meta name="_csrf" content="4bfd1575-3ad1-4d21-96c7-4ef2d9f86721"/>
<meta name="_csrf_header" content="X-CSRF-TOKEN"/>
<!-- ... -->
</head>
<!-- ... -->
一旦元标记包含 CSRF 标记,JavaScript 代码将读取元标记并将 CSRF 标记作为标头包含在内。 如果您使用的是 jQuery,则可以通过以下方法完成此作:
$(function () {
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
$(document).ajaxSend(function(e, xhr, options) {
xhr.setRequestHeader(header, token);
});
});
csrfMeta 标记
如果您使用的是 JSP,则将 CSRF Tokens写入meta
标签是通过利用 csrfMeta 标签。
CsrfToken 请求属性
如果在请求中包含实际 CSRF Tokens的其他选项不起作用,您可以利用以下事实:CsrfToken
被公开为HttpServletRequest
名为_csrf
.
使用 JSP 执行此作的示例如下所示:
<html>
<head>
<meta name="_csrf" content="${_csrf.token}"/>
<!-- default header name is X-CSRF-TOKEN -->
<meta name="_csrf_header" content="${_csrf.headerName}"/>
<!-- ... -->
</head>
<!-- ... -->
CSRF 注意事项
在实施针对 CSRF 攻击的保护时,需要考虑一些特殊注意事项。 本节讨论与 servlet 环境相关的这些注意事项。 请参阅 CSRF 注意事项 以进行更一般的讨论。
登录
请务必对登录请求要求 CSRF,以防止伪造登录尝试。 Spring Security 的 servlet 支持开箱即用地执行此作。
注销
请务必对注销请求要求 CSRF,以防止伪造注销尝试。
如果启用了 CSRF 保护(默认),则 Spring Security 的LogoutFilter
仅处理 HTTP POST。
这可确保注销需要 CSRF Tokens,并且恶意用户无法强制注销您的用户。
最简单的方法是使用表单注销。 如果你真的想要一个链接,你可以使用 JavaScript 让链接执行 POST(即可能在隐藏的表单上)。 对于禁用了 JavaScript 的浏览器,您可以选择让链接将用户带到将执行 POST 的注销确认页面。
如果您真的想在注销时使用 HTTP GET,您可以这样做,但请记住,通常不建议这样做。
例如,以下 Java 配置将使用 URL 执行注销/logout
使用任何 HTTP 方法请求:
-
Java
-
Kotlin
@EnableWebSecurity
public class WebSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.logout(logout -> logout
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
);
return http.build();
}
}
@EnableWebSecurity
class SecurityConfig {
@Bean
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
http {
logout {
logoutRequestMatcher = AntPathRequestMatcher("/logout")
}
}
return http.build()
}
}
CSRF 和会话超时
默认情况下,Spring Security 将 CSRF Tokens存储在HttpSession
.
这可能会导致会话过期的情况,这意味着没有预期的 CSRF Tokens可以验证。
我们已经讨论了会话超时的一般解决方案。 本节讨论与 servlet 支持相关的 CSRF 超时的细节。
将预期的 CSRF Tokens的存储更改为 cookie 中的存储很简单。 有关详细信息,请参阅自定义 CsrfTokenRepository 部分。
分段(文件上传)
有关在 Spring 中使用多部分表单的更多信息,可以在 1.1.11 中找到。Spring 参考的 Multipart Resolver 部分和 MultipartFilter javadoc。 |
将 CSRF Tokens放入正文中
我们已经讨论了将 CSRF Tokens放入正文中的权衡。 在本节中,我们将讨论如何配置 Spring Security 以从正文中读取 CSRF。
为了从正文中读取 CSRF Tokens,MultipartFilter
在 Spring Security 过滤器之前指定。
指定MultipartFilter
之前 Spring Security 过滤器意味着没有调用MultipartFilter
这意味着任何人都可以在您的服务器上放置临时文件。
但是,只有授权用户才能提交由应用程序处理的文件。
通常,这是推荐的方法,因为临时文件上传对大多数服务器的影响应该可以忽略不计。
确保MultipartFilter
在使用 java 配置的 Spring Security 过滤器之前指定,用户可以覆盖 beforeSpringSecurityFilterChain,如下所示:
-
Java
-
Kotlin
public class SecurityApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
@Override
protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
insertFilters(servletContext, new MultipartFilter());
}
}
class SecurityApplicationInitializer : AbstractSecurityWebApplicationInitializer() {
override fun beforeSpringSecurityFilterChain(servletContext: ServletContext?) {
insertFilters(servletContext, MultipartFilter())
}
}
确保MultipartFilter
在使用 XML 配置的 Spring Security 过滤器之前指定,用户可以><确保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>
在 URL 中包含 CSRF Tokens
如果允许未经授权的用户上传临时文件是不可接受的,另一种方法是将MultipartFilter
在 Spring Security 过滤器之后,并将 CSRF 作为查询参数包含在表单的 action 属性中。
由于CsrfToken
被公开为HttpServletRequest
request 属性,我们可以使用它来创建一个action
其中包含 CSRF Tokens。
下面显示了 jsp 的示例
<form method="post"
action="./upload?${_csrf.parameterName}=${_csrf.token}"
enctype="multipart/form-data">
HiddenHttp方法过滤器
我们已经讨论了将 CSRF Tokens放入正文中的权衡。
在 Spring 的 Servlet 支持中,使用 HiddenHttpMethodFilter 覆盖 HTTP 方法。 更多信息可以在参考文档的 HTTP 方法转换部分找到。