对于最新的稳定版本,请使用 Spring Security 6.5.3spring-doc.cadn.net.cn

Servlet 环境的跨站点请求伪造 (CSRF)

本节讨论 Spring Security 对 servlet 环境的跨站点请求伪造 (CSRF) 支持。spring-doc.cadn.net.cn

使用 Spring Security CSRF 保护

下面概述了使用 Spring Security 的 CSRF 保护的步骤:spring-doc.cadn.net.cn

使用正确的 HTTP 动词

防范 CSRF 攻击的第一步是确保您的网站使用正确的 HTTP 动词。安全方法必须是幂等的 中详细介绍了这一点。spring-doc.cadn.net.cn

配置 CSRF 保护

下一步是在应用程序中配置 Spring Security 的 CSRF 保护。默认情况下,Spring Security 的 CSRF 保护处于启用状态,但您可能需要自定义配置。以下是一些常见的自定义。spring-doc.cadn.net.cn

自定义 CsrfTokenRepository

默认情况下,Spring Security 将预期的 CSRF Tokens存储在HttpSessionHttpSessionCsrfTokenRepository. 在某些情况下,用户可能希望配置自定义CsrfTokenRepository. 例如,可能需要保留CsrfToken在 cookie 中支持基于 JavaScript 的应用程序spring-doc.cadn.net.cn

默认情况下,CookieCsrfTokenRepository将写入名为XSRF-TOKEN并从名为X-XSRF-TOKEN或 HTTP 参数_csrf. 这些默认值来自 AngularJSspring-doc.cadn.net.cn

您可以配置CookieCsrfTokenRepository在 XML 中使用以下命令:spring-doc.cadn.net.cn

将 CSRF Tokens存储在具有 XML 配置的 Cookie 中
<http>
	<!-- ... -->
	<csrf token-repository-ref="tokenRepository"/>
</http>
<b:bean id="tokenRepository"
	class="org.springframework.security.web.csrf.CookieCsrfTokenRepository"
	p:cookieHttpOnly="false"/>

示例显式设置cookieHttpOnly=false. 这是允许 JavaScript(即 AngularJS)读取它所必需的。如果您不需要直接使用 JavaScript 读取 cookie 的能力,建议省略cookieHttpOnly=false以提高安全性。spring-doc.cadn.net.cn

您可以配置CookieCsrfTokenRepository在 Java 配置中使用:spring-doc.cadn.net.cn

将 CSRF Tokens存储在 Cookie 中
@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()
    }
}

示例显式设置cookieHttpOnly=false. 这是允许 JavaScript(即 AngularJS)读取它所必需的。如果您不需要直接使用 JavaScript 读取 cookie 的能力,建议省略cookieHttpOnly=false(通过使用new CookieCsrfTokenRepository()相反)以提高安全性。spring-doc.cadn.net.cn

禁用 CSRF 保护

默认情况下启用 CSRF 保护。但是,如果对您的应用程序有意义,则禁用 CSRF 保护很简单。spring-doc.cadn.net.cn

下面的 XML 配置将禁用 CSRF 保护。spring-doc.cadn.net.cn

禁用 CSRF XML 配置
<http>
	<!-- ... -->
	<csrf disabled="true"/>
</http>

下面的 Java 配置将禁用 CSRF 保护。spring-doc.cadn.net.cn

禁用 CSRF
@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-doc.cadn.net.cn

Spring Security 的 CsrfFilterCsrfToken 公开为HttpServletRequest名为_csrf. 这意味着任何视图技术都可以访问CsrfToken将预期的Tokens公开为表单元标记。幸运的是,下面列出了一些集成,使将Tokens包含在表单ajax 请求中变得更加容易。spring-doc.cadn.net.cn

表单 URL 编码

为了发布 HTML 表单,必须将 CSRF Tokens作为隐藏输入包含在表单中。例如,呈现的 HTML 可能如下所示:spring-doc.cadn.net.cn

CSRF Tokens HTML
<input type="hidden"
	name="_csrf"
	value="4bfd1575-3ad1-4d21-96c7-4ef2d9f86721"/>

接下来,我们将讨论将 CSRF Tokens包含在表单中作为隐藏输入的各种方法。spring-doc.cadn.net.cn

自动包含 CSRF Tokens

Spring Security 的 CSRF 支持通过其 CsrfRequestDataValueProcessor 提供与 Spring 的 RequestDataValueProcessor 的集成。这意味着,如果您利用 Spring 的表单标签库Thymeleaf 或任何其他与RequestDataValueProcessor,则具有不安全 HTTP 方法(即 post)的表单将自动包含实际的 CSRF Tokens。spring-doc.cadn.net.cn

csrfInput 标签

如果您使用的是 JSP,那么您可以使用 Spring 的表单标签库。但是,如果这不是一个选项,您也可以轻松地将Tokens包含在 csrfInput 标签中。spring-doc.cadn.net.cn

CsrfToken 请求属性

如果在请求中包含实际 CSRF Tokens的其他选项不起作用,您可以利用以下事实:CsrfToken 被公开HttpServletRequest名为_csrf.spring-doc.cadn.net.cn

使用 JSP 执行此作的示例如下所示:spring-doc.cadn.net.cn

具有请求属性的表单中的 CSRF Tokens
<c:url var="logoutUrl" value="/logout"/>
<form action="${logoutUrl}"
	method="post">
<input type="submit"
	value="Log out" />
<input type="hidden"
	name="${_csrf.parameterName}"
	value="${_csrf.token}"/>
</form>

Ajax 和 JSON 请求

如果您使用的是 JSON,则无法在 HTTP 参数中提交 CSRF Tokens。 相反,您可以在 HTTP 标头中提交Tokens。spring-doc.cadn.net.cn

在以下部分中,我们将讨论在基于 JavaScript 的应用程序中将 CSRF Tokens作为 HTTP 请求标头包含的各种方法。spring-doc.cadn.net.cn

自动包含

可以轻松配置 Spring Security 以将预期的 CSRF Tokens存储在 cookie 中。 通过将预期的 CSRF 存储在 cookie 中,AngularJS 等 JavaScript 框架将自动在 HTTP 请求标头中包含实际的 CSRF Tokens。spring-doc.cadn.net.cn

元标记

cookie 中公开 CSRF 的另一种模式是在meta标签。 HTML 可能如下所示:spring-doc.cadn.net.cn

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,则可以通过以下方法完成此作:spring-doc.cadn.net.cn

AJAX 发送 CSRF Tokens
$(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 标签。spring-doc.cadn.net.cn

CsrfToken 请求属性

如果在请求中包含实际 CSRF Tokens的其他选项不起作用,您可以利用以下事实:CsrfToken 被公开HttpServletRequest名为_csrf. 使用 JSP 执行此作的示例如下所示:spring-doc.cadn.net.cn

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 注意事项 以进行更一般的讨论。spring-doc.cadn.net.cn

登录

请务必对登录请求要求 CSRF,以防止伪造登录尝试。 Spring Security 的 servlet 支持开箱即用地执行此作。spring-doc.cadn.net.cn

注销

请务必对注销请求要求 CSRF,以防止伪造注销尝试。 如果启用了 CSRF 保护(默认),则 Spring Security 的LogoutFilter仅处理 HTTP POST。 这可确保注销需要 CSRF Tokens,并且恶意用户无法强制注销您的用户。spring-doc.cadn.net.cn

最简单的方法是使用表单注销。 如果你真的想要一个链接,你可以使用 JavaScript 让链接执行 POST(即可能在隐藏的表单上)。 对于禁用了 JavaScript 的浏览器,您可以选择让链接将用户带到将执行 POST 的注销确认页面。spring-doc.cadn.net.cn

如果您真的想在注销时使用 HTTP GET,您可以这样做,但请记住,通常不建议这样做。 例如,以下 Java 配置将使用 URL 执行注销/logout使用任何 HTTP 方法请求:spring-doc.cadn.net.cn

使用 HTTP GET 注销
@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可以验证。spring-doc.cadn.net.cn

我们已经讨论了会话超时的一般解决方案。 本节讨论与 servlet 支持相关的 CSRF 超时的细节。spring-doc.cadn.net.cn

将预期的 CSRF Tokens的存储更改为 cookie 中的存储很简单。 有关详细信息,请参阅自定义 CsrfTokenRepository 部分。spring-doc.cadn.net.cn

如果Tokens确实过期,您可能希望通过指定自定义AccessDeniedHandler. 自定义AccessDeniedHandler可以处理InvalidCsrfTokenException任何你喜欢的方式。 有关如何自定义AccessDeniedHandler有关 XMLJava 配置,请参阅提供的链接。spring-doc.cadn.net.cn

分段(文件上传)

我们已经讨论过保护分段请求(文件上传)免受 CSRF 攻击如何导致先有鸡还是先有蛋的问题。 本节讨论如何实现将 CSRF Tokens放置在 Servlet 应用程序中的正文url 中。spring-doc.cadn.net.cn

有关在 Spring 中使用多部分表单的更多信息,可以在 1.1.11 中找到。Spring 参考的 Multipart Resolver 部分和 MultipartFilter javadocspring-doc.cadn.net.cn

将 CSRF Tokens放入正文中

我们已经讨论了将 CSRF Tokens放入正文中的权衡。 在本节中,我们将讨论如何配置 Spring Security 以从正文中读取 CSRF。spring-doc.cadn.net.cn

为了从正文中读取 CSRF Tokens,MultipartFilter在 Spring Security 过滤器之前指定。 指定MultipartFilter之前 Spring Security 过滤器意味着没有调用MultipartFilter这意味着任何人都可以在您的服务器上放置临时文件。 但是,只有授权用户才能提交由应用程序处理的文件。 通常,这是推荐的方法,因为临时文件上传对大多数服务器的影响应该可以忽略不计。spring-doc.cadn.net.cn

确保MultipartFilter在使用 java 配置的 Spring Security 过滤器之前指定,用户可以覆盖 beforeSpringSecurityFilterChain,如下所示:spring-doc.cadn.net.cn

初始值设定项 MultipartFilter
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之前,如下所示:spring-doc.cadn.net.cn

web.xml - MultipartFilter
<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 的示例spring-doc.cadn.net.cn

CSRF Tokens在行动
<form method="post"
	action="./upload?${_csrf.parameterName}=${_csrf.token}"
	enctype="multipart/form-data">

HiddenHttp方法过滤器

我们已经讨论了将 CSRF Tokens放入正文中的权衡。spring-doc.cadn.net.cn

在 Spring 的 Servlet 支持中,使用 HiddenHttpMethodFilter 覆盖 HTTP 方法。 更多信息可以在参考文档的 HTTP 方法转换部分找到。spring-doc.cadn.net.cn