# WebFlux 环境中的跨站点请求伪造
本节讨论 Spring Security 对 WebFlux 环境的跨站点请求伪造支持。
# 使用 Spring 安全 CSRF 保护
使用 Spring Security 的 CSRF 保护的步骤概述如下:
# 使用适当的 HTTP 动词
防止 CSRF 攻击的第一步是确保你的网站使用正确的 HTTP 动词。这在安全的方法必须是幂等的。中有详细介绍。
# 配置 CSRF 保护
下一步是在应用程序中配置 Spring Security 的 CSRF 保护。 Spring 默认情况下,Security 的 CSRF 保护是启用的,但你可能需要定制配置。下面是一些常见的定制。
# 自定义 CSRFTokenRepository
默认情况下, Spring Security 使用WebSessionServerCsrfTokenRepository
将预期的 CSRF 令牌存储在WebSession
中。在某些情况下,用户可能希望配置自定义ServerCsrfTokenRepository
。例如,可能希望将 cookie 中的CsrfToken
持久化到支持基于 爪哇Script 的应用程序。
默认情况下,CookieServerCsrfTokenRepository
将写到一个名为XSRF-TOKEN
的 cookie,并从一个名为X-XSRF-TOKEN
的头部或 HTTP 参数_csrf
读取它。这些默认值来自AngularJS (opens new window)
你可以在 爪哇 配置中使用以下方法配置CookieServerCsrfTokenRepository
:
例 1。在 cookie 中存储 CSRF 令牌
爪哇
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http
// ...
.csrf(csrf -> csrf.csrfTokenRepository(CookieServerCsrfTokenRepository.withHttpOnlyFalse()))
return http.build();
}
Kotlin
@Bean
fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
// ...
csrf {
csrfTokenRepository = CookieServerCsrfTokenRepository.withHttpOnlyFalse()
}
}
}
示例显式设置cookieHttpOnly=false .这是允许 爪哇Script(即 Angularjs)读取它所必需的。 如果不需要直接使用 爪哇Script 读取 cookie 的能力,建议省略 cookieHttpOnly=false (通过使用new CookieServerCsrfTokenRepository() 代替)以提高安全性。 |
---|
# 禁用 CSRF 保护
默认情况下启用了 CSRF 保护。但是,如果 CSRF 保护对你的应用程序来说是有意义的,则禁用 CSRF 保护非常简单。
下面的 Java 配置将禁用 CSRF 保护。
例 2。禁用 CSRF 配置
Java
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http
// ...
.csrf(csrf -> csrf.disable()))
return http.build();
}
Kotlin
@Bean
fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
// ...
csrf {
disable()
}
}
}
# 包括 CSRF 令牌
为了使同步器令牌模式能够抵御 CSRF 攻击,我们必须在 HTTP 请求中包含实际的 CSRF 令牌。这必须包含在请求的一部分(即表单参数、HTTP 头等)中,而该部分不是由浏览器自动包含在 HTTP 请求中的。
Spring Security 的CSRFWebfilter (opens new window)将Mono<CsrfToken> (opens new window)公开为名为ServerWebExchange
的属性。这意味着,任何视图技术都可以访问Mono<CsrfToken>
以将预期的令牌公开为form或meta tag。
如果你的视图技术没有提供订阅Mono<CsrfToken>
的简单方法,那么一个常见的模式是使用 Spring 的@ControllerAdvice
直接公开CsrfToken
。例如,以下代码将在 Spring Security 的CsrfrequestDataValueProcessor 使用的默认属性名(_csrf
)上放置CsrfToken
,以自动将 CSRF 令牌作为隐藏输入。
例 3。CsrfToken
as@ModelAttribute
Java
@ControllerAdvice
public class SecurityControllerAdvice {
@ModelAttribute
Mono<CsrfToken> csrfToken(ServerWebExchange exchange) {
Mono<CsrfToken> csrfToken = exchange.getAttribute(CsrfToken.class.getName());
return csrfToken.doOnSuccess(token -> exchange.getAttributes()
.put(CsrfRequestDataValueProcessor.DEFAULT_CSRF_ATTR_NAME, token));
}
}
Kotlin
@ControllerAdvice
class SecurityControllerAdvice {
@ModelAttribute
fun csrfToken(exchange: ServerWebExchange): Mono<CsrfToken> {
val csrfToken: Mono<CsrfToken>? = exchange.getAttribute(CsrfToken::class.java.name)
return csrfToken!!.doOnSuccess { token ->
exchange.attributes[CsrfRequestDataValueProcessor.DEFAULT_CSRF_ATTR_NAME] = token
}
}
}
幸运的是,ThymeLeaf 提供了整合,它的工作不需要任何额外的工作。
# 表单 URL 编码
为了发布 HTML 表单,CSRF 令牌必须作为隐藏输入包含在表单中。例如,呈现的 HTML 可能看起来像:
例 4。CSRF 令牌 HTML
<input type="hidden"
name="_csrf"
value="4bfd1575-3ad1-4d21-96c7-4ef2d9f86721"/>
接下来,我们将讨论将 CSRF 令牌以一种形式包含为隐藏输入的各种方法。
# CSRF 令牌自动包含
Spring Security 的 CSRF 支持通过其CsrfrequestDataValueProcessor (opens new window)提供与 Spring 的RequestDataValueProcessor (opens new window)的集成。为了使CsrfRequestDataValueProcessor
工作,必须订阅Mono<CsrfToken>
,并且CsrfToken
必须是与作为属性公开匹配的默认 _CSRF_ATTR_Name (opens new window)。
幸运的是,ThymeLeaf提供支持 (opens new window)通过与RequestDataValueProcessor
集成来为你处理所有的样板文件,以确保具有不安全的 HTTP 方法(即 POST)的窗体将自动包括实际的 CSRF 令牌。
# CSRFToken 请求属性
如果用于在请求中包含实际 CSRF 令牌的其他选择不起作用,则可以利用以下事实:Mono<CsrfToken>
is exposed作为ServerWebExchange
属性,该属性名为org.springframework.security.web.server.csrf.CsrfToken
。
下面的 ThymeLeaf 示例假设你在一个名为_csrf
的属性上expose。
例 5。具有请求属性的表单中的 CSRF 令牌
<form th:action="@{/logout}"
method="post">
<input type="submit"
value="Log out" />
<input type="hidden"
th:name="${_csrf.parameterName}"
th:value="${_csrf.token}"/>
</form>
# Ajax 和 JSON 请求
如果你正在使用 JSON,那么就不可能在 HTTP 参数中提交 CSRF 令牌。相反,你可以在 HTTP 头中提交令牌。
在下面的部分中,我们将讨论在基于 JavaScript 的应用程序中将 CSRF 令牌作为 HTTP 请求头包含在内的各种方法。
# 自动包含
Spring 安全性可以很容易地configured将预期的 CSRF 令牌存储在 cookie 中。通过将预期的 CSRF 存储在 Cookie 中,像AngularJS (opens new window)这样的 JavaScript 框架将自动在 HTTP 请求头中包含实际的 CSRF 令牌。
# 元标签
在 cookie 中暴露 CSRF 的另一种模式是在meta
标记中包含 CSRF 标记。HTML 可能看起来是这样的:
例 6。CSRF 元标记 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,可以通过以下方式完成此操作:
例 7。Ajax 发送 CSRF 令牌
$(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);
});
});
下面的示例假设你在名为_csrf
的属性上CsrfToken
。使用 Thymeleaf 进行此操作的示例如下所示:
例 8。CSRF 元标记 JSP
<html>
<head>
<meta name="_csrf" th:content="${_csrf.token}"/>
<!-- default header name is X-CSRF-TOKEN -->
<meta name="_csrf_header" th:content="${_csrf.headerName}"/>
<!-- ... -->
</head>
<!-- ... -->
# CSRF 考虑因素
在实施针对 CSRF 攻击的保护时,有几个特殊的考虑因素需要考虑。本节讨论了与 WebFlux 环境相关的那些注意事项。有关更一般的讨论,请参见CSRF 考虑因素。
# 登录
这是重要的需要 CSRF 才能登录请求,以防止伪造日志的企图。 Spring Security 的 WebFlux 支持提供了开箱即用的服务。
# 注销
重要的是需要 CSRF 才能注销请求,以防止伪造注销尝试。默认情况下 Spring Security 的LogoutWebFilter
只处理 HTTP POST 请求。这确保了注销需要 CSRF 令牌,并且恶意用户不能强制注销你的用户。
最简单的方法是使用表单注销。如果你真的想要一个链接,可以使用 JavaScript 让该链接执行一个 POST(例如,可能在一个隐藏的表单上)。对于禁用了 JavaScript 的浏览器,你可以选择让链接将用户带到将执行 POST 的注销确认页面。
如果你真的想使用 HTTP GET 与注销,你可以这样做,但请记住,这通常是不推荐的。例如,下面的 Java 配置将使用任何 HTTP 方法请求的 URL/logout
执行注销:
例 9。用 HTTP GET 登出
Java
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http
// ...
.logout(logout -> logout.requiresLogout(new PathPatternParserServerWebExchangeMatcher("/logout")))
return http.build();
}
Kotlin
@Bean
fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
// ...
logout {
requiresLogout = PathPatternParserServerWebExchangeMatcher("/logout")
}
}
}
# CSRF 和会话暂停
默认情况下, Spring Security 将 CSRF 令牌存储在WebSession
中。这可能导致会话过期的情况,这意味着没有预期的 CSRF 令牌来验证。
我们已经讨论了一般解决方案到会话的超时。本节讨论 CSRF 超时的细节,因为它与 WebFlux 支持有关。
将预期的 CSRF 令牌的存储更改为 cookie 中的存储是很简单的。有关详细信息,请参阅自定义 CSRFTokenRepository 部分。
#
我们有已经讨论过了如何保护多部分请求(文件上传)不受 CSRF 攻击导致鸡和蛋 (opens new window)问题。本节讨论如何在 WebFlux 应用程序中实现将 CSRF 令牌放置在body和url中。
关于使用具有 Spring 的多部分表单的更多信息可以在 Spring 引用的多部分数据 (opens new window)部分中找到。 |
---|
# 将 CSRF 标记放入体内
我们有已经讨论过了在主体中放置 CSRF 标记的权衡。
在 WebFlux 应用程序中,可以使用以下配置对其进行配置:
例 10。启用从多部分/表单数据获取 CSRF 令牌
Java
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http
// ...
.csrf(csrf -> csrf.tokenFromMultipartDataEnabled(true))
return http.build();
}
Kotlin
@Bean
fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
// ...
csrf {
tokenFromMultipartDataEnabled = true
}
}
}
# 在 URL 中包含 CSRF 令牌
我们有已经讨论过了在 URL 中放置 CSRF 标记的权衡。由于CsrfToken
被公开为ServerHttpRequest
请求属性,因此我们可以使用它来创建带有 CSRF 令牌的action
。ThymeLeaf 的一个示例如下所示:
例 11。CSRF 令牌正在运行
<form method="post"
th:action="@{/upload(${_csrf.parameterName}=${_csrf.token})}"
enctype="multipart/form-data">
# HiddenHttpMethodFilter
我们已经已经讨论过了覆盖了 HTTP 方法。
在 Spring WebFlux 应用程序中,使用HiddenHttpMethodFilter (opens new window)重写 HTTP 方法。
← 保护免受剥削 安全 HTTP 响应标头 →