|
此版本仍在开发中,尚未被视为稳定版本。如需最新稳定版本,请使用 Spring Security 7.0.4! |
安全 HTTP 响应头
您可以使用安全 HTTP 响应头来提升 Web 应用程序的安全性。 本节专门介绍基于 Servlet 的安全 HTTP 响应头支持。
默认安全头
Spring Security 提供了一组默认的安全 HTTP 响应头,以提供安全的默认配置。 尽管这些响应头均被视为最佳实践,但需要注意的是,并非所有客户端都会使用这些响应头,因此建议进行额外的测试。
您可以自定义特定的头部。
例如,假设您希望使用默认设置,但希望为 X-Frame-Options 指定 #servlet-headers-frame-options。
你可以通过以下配置来实现:
-
Java
-
XML
-
Kotlin
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// ...
.headers((headers) -> headers
.frameOptions((frameOptions) -> frameOptions
.sameOrigin()
)
);
return http.build();
}
}
<http>
<!-- ... -->
<headers>
<frame-options policy="SAMEORIGIN" />
</headers>
</http>
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
http {
// ...
headers {
frameOptions {
sameOrigin = true
}
}
}
return http.build()
}
}
如果你不希望添加默认配置,并希望显式控制应使用的内容,可以禁用这些默认配置。 下面的代码清单展示了如何实现这一点。
如果你使用 Spring Security 的配置,以下内容仅添加缓存控制(Cache Control):
-
Java
-
XML
-
Kotlin
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// ...
.headers((headers) -> headers
// do not use any default headers unless explicitly listed
.defaultsDisabled()
.cacheControl(withDefaults())
);
return http.build();
}
}
<http>
<!-- ... -->
<headers defaults-disabled="true">
<cache-control/>
</headers>
</http>
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
http {
// ...
headers {
// do not use any default headers unless explicitly listed
defaultsDisabled = true
cacheControl {
}
}
}
return http.build()
}
}
如有必要,您可以使用以下配置禁用所有 HTTP 安全响应头:
-
Java
-
XML
-
Kotlin
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// ...
.headers((headers) -> headers.disable());
return http.build();
}
}
<http>
<!-- ... -->
<headers disabled="true" />
</http>
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
http {
// ...
headers {
disable()
}
}
return http.build()
}
}
缓存控制
Spring Security 默认包含缓存控制头部。
然而,如果您确实希望缓存特定响应,您的应用程序可以有选择地调用 HttpServletResponse.setHeader(String,String) 来覆盖 Spring Security 设置的标头。
您可以使用此功能确保内容(如 CSS、JavaScript 和图像)得到正确缓存。
当你使用 Spring Web MVC 时,这通常在你的配置中完成。 你可以在 Spring 参考文档的静态资源部分找到有关如何执行此操作的详细信息。
如有必要,您也可以禁用 Spring Security 的缓存控制 HTTP 响应头。
-
Java
-
XML
-
Kotlin
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// ...
.headers((headers) -> headers
.cacheControl((cache) -> cache.disable())
);
return http.build();
}
}
<http>
<!-- ... -->
<headers>
<cache-control disabled="true"/>
</headers>
</http>
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
http {
headers {
cacheControl {
disable()
}
}
}
return http.build()
}
}
内容类型选项
Spring Security 默认包含 Content-Type 响应头。 但你可以将其禁用:
-
Java
-
XML
-
Kotlin
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// ...
.headers((headers) -> headers
.contentTypeOptions((contentTypeOptions) -> contentTypeOptions.disable())
);
return http.build();
}
}
<http>
<!-- ... -->
<headers>
<content-type-options disabled="true"/>
</headers>
</http>
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
http {
headers {
contentTypeOptions {
disable()
}
}
}
return http.build()
}
}
HTTP 严格传输安全 (HSTS)
默认情况下,Spring Security 提供 Strict Transport Security(严格传输安全)头部。 不过,您可以显式地自定义该头部的内容。 以下示例显式地提供了 HSTS:
-
Java
-
XML
-
Kotlin
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// ...
.headers((headers) -> headers
.httpStrictTransportSecurity((hsts) -> hsts
.includeSubDomains(true)
.preload(true)
.maxAgeInSeconds(31536000)
)
);
return http.build();
}
}
<http>
<!-- ... -->
<headers>
<hsts
include-subdomains="true"
max-age-seconds="31536000"
preload="true" />
</headers>
</http>
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
http {
headers {
httpStrictTransportSecurity {
includeSubDomains = true
preload = true
maxAgeInSeconds = 31536000
}
}
}
return http.build()
}
}
HTTP 公钥固定 (HPKP)
Spring Security 为 HTTP 公钥固定(HTTP Public Key Pinning) 提供了 Servlet 支持,但该功能已不再推荐使用。
你可以通过以下配置启用 HPKP 响应头:
-
Java
-
XML
-
Kotlin
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// ...
.headers((headers) -> headers
.httpPublicKeyPinning((hpkp) -> hpkp
.includeSubDomains(true)
.reportUri("https://example.net/pkp-report")
.addSha256Pins("d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=", "E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=")
)
);
return http.build();
}
}
<http>
<!-- ... -->
<headers>
<hpkp
include-subdomains="true"
report-uri="https://example.net/pkp-report">
<pins>
<pin algorithm="sha256">d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=</pin>
<pin algorithm="sha256">E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=</pin>
</pins>
</hpkp>
</headers>
</http>
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
http {
headers {
httpPublicKeyPinning {
includeSubDomains = true
reportUri = "https://example.net/pkp-report"
pins = mapOf("d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=" to "sha256",
"E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=" to "sha256")
}
}
}
return http.build()
}
}
X-Frame-Options
默认情况下,Spring Security 通过使用 X-Frame-Options 指示浏览器阻止反射式 XSS 攻击。
例如,以下配置指定 Spring Security 不应再指示浏览器阻止该内容:
-
Java
-
XML
-
Kotlin
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// ...
.headers((headers) -> headers
.frameOptions((frameOptions) -> frameOptions
.sameOrigin()
)
);
return http.build();
}
}
<http>
<!-- ... -->
<headers>
<frame-options
policy="SAMEORIGIN" />
</headers>
</http>
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
http {
headers {
frameOptions {
sameOrigin = true
}
}
}
return http.build()
}
}
X-XSS-Protection
默认情况下,Spring Security 使用 <headers-xss-protection,X-XSS-Protection 头指示浏览器禁用 XSS 审计器。 然而,您可以更改此默认设置。 例如,以下配置指定了 Spring Security 告诉兼容的浏览器启用过滤并阻止内容:
-
Java
-
XML
-
Kotlin
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// ...
.headers((headers) -> headers
.xssProtection((xss) -> xss
.headerValue(XXssProtectionHeaderWriter.HeaderValue.ENABLED_MODE_BLOCK)
)
);
return http.build();
}
}
<http>
<!-- ... -->
<headers>
<xss-protection headerValue="1; mode=block"/>
</headers>
</http>
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
// ...
http {
headers {
xssProtection {
headerValue = XXssProtectionHeaderWriter.HeaderValue.ENABLED_MODE_BLOCK
}
}
}
return http.build()
}
}
内容安全策略 (CSP)
Spring Security 默认不会添加内容安全策略(Content Security Policy),因为在不了解应用程序上下文的情况下,无法确定一个合理的默认策略。 Web 应用程序的开发者必须为受保护的资源声明要强制执行或监控的安全策略(或多个策略)。
请考虑以下安全策略:
Content-Security-Policy: script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/
根据上述安全策略,您可以启用 CSP(内容安全策略)头部:
-
Java
-
XML
-
Kotlin
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// ...
.headers((headers) -> headers
.contentSecurityPolicy((csp) -> csp
.policyDirectives("script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/")
)
);
return http.build();
}
}
<http>
<!-- ... -->
<headers>
<content-security-policy
policy-directives="script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/" />
</headers>
</http>
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
http {
// ...
headers {
contentSecurityPolicy {
policyDirectives = "script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/"
}
}
}
return http.build()
}
}
要启用 CSP report-only 头部,请提供以下配置:
-
Java
-
XML
-
Kotlin
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// ...
.headers((headers) -> headers
.contentSecurityPolicy((csp) -> csp
.policyDirectives("script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/")
.reportOnly()
)
);
return http.build();
}
}
<http>
<!-- ... -->
<headers>
<content-security-policy
policy-directives="script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/"
report-only="true" />
</headers>
</http>
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
http {
// ...
headers {
contentSecurityPolicy {
policyDirectives = "script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/"
reportOnly = true
}
}
}
return http.build()
}
}
引用来源策略
Spring Security 默认不会添加 Referrer Policy(引用来源策略) 头。 您可以通过以下配置启用 Referrer Policy 头:
-
Java
-
XML
-
Kotlin
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// ...
.headers((headers) -> headers
.referrerPolicy((referrer) -> referrer
.policy(ReferrerPolicy.SAME_ORIGIN)
)
);
return http.build();
}
}
<http>
<!-- ... -->
<headers>
<referrer-policy policy="same-origin" />
</headers>
</http>
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
http {
// ...
headers {
referrerPolicy {
policy = ReferrerPolicy.SAME_ORIGIN
}
}
}
return http.build()
}
}
功能策略
Spring Security 默认不会添加 Feature Policy 头。
请考虑以下 Feature-Policy 头:
Feature-Policy: geolocation 'self'
您可以通过以下配置启用上述功能策略头:
-
Java
-
XML
-
Kotlin
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// ...
.headers((headers) -> headers
.featurePolicy("geolocation 'self'")
);
return http.build();
}
}
<http>
<!-- ... -->
<headers>
<feature-policy policy-directives="geolocation 'self'" />
</headers>
</http>
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
http {
// ...
headers {
featurePolicy("geolocation 'self'")
}
}
return http.build()
}
}
权限策略
Spring Security 默认不会添加 Permissions Policy 响应头。
请考虑以下 Permissions-Policy 响应头:
Permissions-Policy: geolocation=(self)
您可以使用以下配置启用上述权限策略标头:
-
Java
-
XML
-
Kotlin
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// ...
.headers((headers) -> headers
.permissionsPolicy((permissions) -> permissions
.policy("geolocation=(self)")
)
);
return http.build();
}
}
<http>
<!-- ... -->
<headers>
<permissions-policy policy="geolocation=(self)" />
</headers>
</http>
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
http {
// ...
headers {
permissionPolicy {
policy = "geolocation=(self)"
}
}
}
return http.build()
}
}
清除站点数据
Spring Security 默认不会添加 Clear-Site-Data 头。 请考虑以下 Clear-Site-Data 头:
Clear-Site-Data: "cache", "cookies"
你可以通过以下配置在注销时发送上述标头:
-
Java
-
Kotlin
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// ...
.logout((logout) -> logout
.addLogoutHandler(new HeaderWriterLogoutHandler(new ClearSiteDataHeaderWriter(CACHE, COOKIES)))
);
return http.build();
}
}
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
http {
// ...
logout {
addLogoutHandler(HeaderWriterLogoutHandler(ClearSiteDataHeaderWriter(CACHE, COOKIES)))
}
}
return http.build()
}
}
自定义请求头
Spring Security 提供了多种机制,便于为您的应用程序添加更常见的安全头。 然而,它也提供了钩子(hooks),以支持添加自定义头。
静态头部
有时,您可能希望将一些框架本身不直接支持的自定义安全头信息注入到您的应用程序中。 请考虑以下自定义安全头:
X-Custom-Security-Header: header-value
根据前述标头,您可以使用以下配置将这些标头添加到响应中:
-
Java
-
XML
-
Kotlin
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// ...
.headers((headers) -> headers
.addHeaderWriter(new StaticHeadersWriter("X-Custom-Security-Header","header-value"))
);
return http.build();
}
}
<http>
<!-- ... -->
<headers>
<header name="X-Custom-Security-Header" value="header-value"/>
</headers>
</http>
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
http {
// ...
headers {
addHeaderWriter(StaticHeadersWriter("X-Custom-Security-Header","header-value"))
}
}
return http.build()
}
}
响应头写入器
当命名空间或 Java 配置不支持您所需的 HTTP 头时,您可以创建一个自定义的 HeadersWriter 实例,甚至提供 HeadersWriter 的自定义实现。
下一个示例使用了 XFrameOptionsHeaderWriter 的自定义实例。
如果你想要显式地配置 X-_Frame_Options,可以使用以下配置:
-
Java
-
XML
-
Kotlin
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// ...
.headers((headers) -> headers
.addHeaderWriter(new XFrameOptionsHeaderWriter(XFrameOptionsMode.SAMEORIGIN))
);
return http.build();
}
}
<http>
<!-- ... -->
<headers>
<header ref="frameOptionsWriter"/>
</headers>
</http>
<!-- Requires the c-namespace.
See https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#beans-c-namespace
-->
<beans:bean id="frameOptionsWriter"
class="org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter"
c:frameOptionsMode="SAMEORIGIN"/>
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
http {
// ...
headers {
addHeaderWriter(XFrameOptionsHeaderWriter(XFrameOptionsMode.SAMEORIGIN))
}
}
return http.build()
}
}
委托请求匹配器头部写入器
有时,你可能只想对某些特定的请求写入响应头。
例如,也许你只想保护你的登录页面不被嵌入到框架(frame)中。
你可以使用 DelegatingRequestMatcherHeaderWriter 来实现这一点。
以下配置示例使用了 DelegatingRequestMatcherHeaderWriter:
-
Java
-
XML
-
Kotlin
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
RequestMatcher matcher = PathPatternRequestMatcher.withDefaults().matcher("/login");
DelegatingRequestMatcherHeaderWriter headerWriter =
new DelegatingRequestMatcherHeaderWriter(matcher,new XFrameOptionsHeaderWriter());
http
// ...
.headers((headers) -> headers
.frameOptions((frameOptions) -> frameOptions.disable())
.addHeaderWriter(headerWriter)
);
return http.build();
}
}
<http>
<!-- ... -->
<headers>
<frame-options disabled="true"/>
<header ref="headerWriter"/>
</headers>
</http>
<beans:bean id="headerWriter"
class="org.springframework.security.web.header.writers.DelegatingRequestMatcherHeaderWriter">
<beans:constructor-arg>
<bean class="org.springframework.security.config.http.PathPatternRequestMatcherFactoryBean"
c:pattern="/login"/>
</beans:constructor-arg>
<beans:constructor-arg>
<beans:bean
class="org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter"/>
</beans:constructor-arg>
</beans:bean>
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
val matcher: RequestMatcher = PathPatternRequestMatcher.withDefaults().matcher("/login")
val headerWriter = DelegatingRequestMatcherHeaderWriter(matcher, XFrameOptionsHeaderWriter())
http {
headers {
frameOptions {
disable()
}
addHeaderWriter(headerWriter)
}
}
return http.build()
}
}