对于最新的稳定版本,请使用 Spring Security 6.3.1Spring中文文档

对于最新的稳定版本,请使用 Spring Security 6.3.1Spring中文文档

本节讨论 Spring Security 对 servlet 环境的跨站点请求伪造 (CSRF) 支持。Spring中文文档

使用 Spring Security CSRF 保护

使用 Spring Security 的 CSRF 保护的步骤概述如下:Spring中文文档

使用正确的 HTTP 谓词

防范 CSRF 攻击的第一步是确保您的网站使用正确的 HTTP 谓词。 安全方法必须是只读中对此进行了详细介绍。Spring中文文档

配置 CSRF 保护

下一步是在应用程序中配置Spring Security的CSRF保护。 默认情况下,Spring Security 的 CSRF 保护处于启用状态,但您可能需要自定义配置。 下面是一些常见的自定义设置。Spring中文文档

自定义 CsrfTokenRepository

默认情况下,Spring Security 将预期的 CSRF 令牌存储在 using . 在某些情况下,用户可能希望配置自定义 . 例如,可能需要在 cookie 中保留 to 支持基于 JavaScript 的应用程序HttpSessionHttpSessionCsrfTokenRepositoryCsrfTokenRepositoryCsrfTokenSpring中文文档

默认情况下,将写入名为 或 HTTP 参数的标头并读取它。 这些默认值来自 AngularJSCookieCsrfTokenRepositoryXSRF-TOKENX-XSRF-TOKEN_csrfSpring中文文档

您可以使用以下命令在 XML 中进行配置:CookieCsrfTokenRepositorySpring中文文档

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

该示例显式设置 。 这是允许 JavaScript(即 AngularJS)读取它所必需的。 如果您不需要直接使用 JavaScript 读取 cookie 的功能,建议省略以提高安全性。cookieHttpOnly=falsecookieHttpOnly=falseSpring中文文档

您可以使用以下命令在 Java 配置中进行配置:CookieCsrfTokenRepositorySpring中文文档

将 CSRF 令牌存储在 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()
    }
}

该示例显式设置 。 这是允许 JavaScript(即 AngularJS)读取它所必需的。 如果您不需要直接使用 JavaScript 读取 cookie 的功能,建议省略(改用)以提高安全性。cookieHttpOnly=falsecookieHttpOnly=falsenew CookieCsrfTokenRepository()Spring中文文档

禁用 CSRF 保护

默认情况下,CSRF 保护处于启用状态。 但是,如果 CSRF 保护对您的应用程序有意义,则禁用 CSRF 保护很简单。Spring中文文档

下面的 XML 配置将禁用 CSRF 保护。Spring中文文档

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

下面的 Java 配置将禁用 CSRF 保护。Spring中文文档

禁用 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()
    }
}

配置 CsrfTokenRequestHandler

Spring Security 的 CsrfFilterCsrfToken 公开为在 CsrfTokenRequestHandler 的帮助下命名的属性。 默认实现为 。HttpServletRequest_csrfCsrfTokenRequestAttributeHandlerSpring中文文档

另一种实现可用于为 BREACH 提供保护(参见 gh-4001)。XorCsrfTokenRequestAttributeHandlerSpring中文文档

您可以使用以下命令在 XML 中进行配置:XorCsrfTokenRequestAttributeHandlerSpring中文文档

配置 BREACH 保护 XML 配置
<http>
	<!-- ... -->
	<csrf request-handler-ref="requestHandler"/>
</http>
<b:bean id="requestHandler"
	class="org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler"/>

您可以使用以下命令在 Java 配置中进行配置:XorCsrfTokenRequestAttributeHandlerSpring中文文档

配置 BREACH 保护
@EnableWebSecurity
public class WebSecurityConfig {

	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http
			.csrf(csrf -> csrf
				.csrfTokenRequestHandler(new XorCsrfTokenRequestAttributeHandler())
			);
		return http.build();
	}
}
@EnableWebSecurity
class SecurityConfig {

    @Bean
    open fun filterChain(http: HttpSecurity): SecurityFilterChain {
       http {
            csrf {
                csrfTokenRequestHandler = XorCsrfTokenRequestAttributeHandler()
            }
        }
        return http.build()
    }
}

包括 CSRF 令牌

为了使同步器令牌模式能够防止 CSRF 攻击,我们必须在 HTTP 请求中包含实际的 CSRF 令牌。 这必须包含在请求的一部分(即表单参数、HTTP 标头等)中,而浏览器不会自动包含在 HTTP 请求中。Spring中文文档

我们已经看到 被公开为请求属性。 这意味着任何视图技术都可以访问以将预期的令牌公开为表单元标记。 幸运的是,下面列出了一些集成,可以更轻松地将令牌包含在表单ajax 请求中。CsrfTokenCsrfTokenSpring中文文档

表单 URL 编码

为了发布 HTML 表单,CSRF 令牌必须作为隐藏输入包含在表单中。 例如,呈现的 HTML 可能如下所示:Spring中文文档

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

接下来,我们将讨论将 CSRF 代币作为隐藏输入包含在表单中的各种方法。Spring中文文档

自动包含 CSRF 代币

Spring Security 的 CSRF 支持通过其 CsrfRequestDataValueProcessor 提供与 Spring 的 RequestDataValueProcessor 的集成。 这意味着,如果你利用 Spring 的表单标签库Thymeleaf 或任何其他与 集成的视图技术,那么具有不安全的 HTTP 方法(即 post)的表单将自动包含实际的 CSRF 令牌。RequestDataValueProcessorSpring中文文档

csrfInput 标记

如果你使用的是JSP,那么你可以使用Spring的表单标签库。 但是,如果这不是一个选项,您也可以轻松地将令牌包含在 csrfInput 标记中。Spring中文文档

CsrfToken 请求属性

如果用于在请求中包含实际 CSRF 令牌的其他选项不起作用,则可以利用 公开为名为 的属性这一事实。CsrfTokenHttpServletRequest_csrfSpring中文文档

下面显示了使用 JSP 执行此操作的示例:Spring中文文档

具有请求属性的表单中的 CSRF 令牌
<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 令牌。 相反,您可以在 HTTP 标头中提交令牌。Spring中文文档

在以下各节中,我们将讨论在基于 JavaScript 的应用程序中将 CSRF 令牌作为 HTTP 请求标头包含的各种方法。Spring中文文档

自动包含

Spring Security 可以很容易地配置为将预期的 CSRF 令牌存储在 cookie 中。 通过将预期的 CSRF 存储在 cookie 中,像 AngularJS 这样的 JavaScript 框架会自动在 HTTP 请求标头中包含实际的 CSRF 令牌。Spring中文文档

元标记

在 Cookie 中公开 CSRF 的另一种模式是在您的标签中包含 CSRF 令牌。 HTML 可能如下所示:metaSpring中文文档

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中文文档

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);
	});
});
csrfMeta 标记

如果您使用的是 JSP,那么将 CSRF 令牌写入标签的简单方法是利用 csrfMeta 标签。metaSpring中文文档

CsrfToken 请求属性

如果用于在请求中包含实际 CSRF 令牌的其他选项不起作用,则可以利用 公开为名为 的属性这一事实。 下面显示了使用 JSP 执行此操作的示例:CsrfTokenHttpServletRequest_csrfSpring中文文档

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>
<!-- ... -->

该示例显式设置 。 这是允许 JavaScript(即 AngularJS)读取它所必需的。 如果您不需要直接使用 JavaScript 读取 cookie 的功能,建议省略以提高安全性。cookieHttpOnly=falsecookieHttpOnly=falseSpring中文文档

该示例显式设置 。 这是允许 JavaScript(即 AngularJS)读取它所必需的。 如果您不需要直接使用 JavaScript 读取 cookie 的功能,建议省略(改用)以提高安全性。cookieHttpOnly=falsecookieHttpOnly=falsenew CookieCsrfTokenRepository()Spring中文文档

CSRF 注意事项

在实施针对 CSRF 攻击的防护时,需要考虑一些特殊注意事项。 本节讨论与 servlet 环境相关的这些注意事项。 有关更一般的讨论,请参阅 CSRF 注意事项Spring中文文档

登录

对于登录请求,需要 CSRF 以防止伪造登录尝试非常重要。 Spring Security 的 servlet 支持开箱即用。Spring中文文档

注销

对于注销请求,需要 CSRF 以防止伪造注销尝试,这一点很重要。 如果启用了CSRF保护(默认),则Spring Security将仅处理HTTP POST。 这可确保注销需要 CSRF 令牌,并且恶意用户无法强制注销您的用户。LogoutFilterSpring中文文档

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

如果您真的想在注销时使用 HTTP GET,您可以这样做,但请记住,通常不建议这样做。 例如,以下 Java 配置将执行注销,并使用任何 HTTP 方法请求的 URL:/logoutSpring中文文档

使用 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 令牌存储在 . 这可能会导致会话过期的情况,这意味着没有预期的 CSRF 令牌要验证。HttpSessionSpring中文文档

我们已经讨论了会话超时的一般解决方案。 本节讨论 CSRF 超时的细节,因为它与 servlet 支持有关。Spring中文文档

将预期 CSRF 令牌的存储更改为 cookie 中很简单。 有关详细信息,请参阅自定义 CsrfTokenRepository 部分。Spring中文文档

如果令牌确实过期,您可能希望通过指定自定义 . 自定义可以按照您喜欢的任何方式进行处理。 有关如何自定义的示例,请参阅为 xmlJava 配置提供的链接。AccessDeniedHandlerAccessDeniedHandlerInvalidCsrfTokenExceptionAccessDeniedHandlerSpring中文文档

分片(文件上传)

我们已经讨论过如何保护多部分请求(文件上传)免受 CSRF 攻击,从而导致先有鸡还是先有蛋的问题。 本节讨论如何实现将 CSRF 令牌放置在 servlet 应用程序的正文url 中。Spring中文文档

有关在 Spring 中使用多部分表单的更多信息,请参见 1.1.11。Spring 参考的 Multipart Resolver 部分和 MultipartFilter javadocSpring中文文档

将 CSRF 令牌放在正文中

我们已经讨论了将 CSRF 代币放在正文中的权衡。 在本节中,我们将讨论如何配置 Spring Security 以从正文中读取 CSRF。Spring中文文档

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

为了确保在使用 java 配置的 Spring Security 过滤器之前指定,用户可以覆盖 beforeSpringSecurityFilterChain,如下所示:MultipartFilterSpring中文文档

初始值设定项 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())
    }
}

为了确保在具有XML配置的Spring Security过滤器之前指定,用户可以确保将web.xml中的<filter-mapping>元素放在springSecurityFilterChain之前,如下所示:MultipartFilterMultipartFilterSpring中文文档

web.xml - 多部分过滤器
<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 令牌

如果不允许未经授权的用户上传临时文件是不可接受的,另一种方法是将 放在 Spring Security 过滤器之后,并将 CSRF 作为查询参数包含在表单的 action 属性中。 由于 是作为请求属性公开的,因此我们可以使用它来创建一个包含 CSRF 令牌的 CSRF 令牌。 下面显示了一个带有 jsp 的示例MultipartFilterCsrfTokenHttpServletRequestactionSpring中文文档

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

HiddenHttpMethodFilter

我们已经讨论了将 CSRF 代币放入正文中的权衡。Spring中文文档

在 Spring 的 Servlet 支持中,使用 HiddenHttpMethodFilter 覆盖 HTTP 方法。 有关详细信息,请参阅参考文档的 HTTP 方法转换部分。Spring中文文档

有关在 Spring 中使用多部分表单的更多信息,请参见 1.1.11。Spring 参考的 Multipart Resolver 部分和 MultipartFilter javadocSpring中文文档