# 安全HTTP响应标头

安全HTTP响应标头可以用来增加Web应用程序的安全性。本节专门讨论基于 Servlet 的对安全HTTP响应头的支持。

# 默认安全标头

Spring 安全性提供了默认的安全HTTP响应标头集以提供安全的默认值。虽然这些标题中的每一个都被认为是最佳实践,但应该注意的是,并不是所有的客户机都使用这些标题,因此鼓励进行额外的测试。

你可以自定义特定的标题。例如,假设你希望使用默认值,但不希望为X-帧-选项指定SAMEORIGIN

你可以通过以下配置轻松地实现这一点:

例1.自定义默认安全标头

Java

@EnableWebSecurity
public class WebSecurityConfig extends
		WebSecurityConfigurerAdapter {

	@Override
	protected void configure(HttpSecurity http) {
		http
			// ...
			.headers(headers -> headers
				.frameOptions(frameOptions -> frameOptions
					.sameOrigin()
				)
			);
	}
}

XML

<http>
	<!-- ... -->

	<headers>
		<frame-options policy="SAMEORIGIN" />
	</headers>
</http>

Kotlin

@EnableWebSecurity
class SecurityConfig : WebSecurityConfigurerAdapter() {
    override fun configure(http: HttpSecurity) {
        http {
            // ...
            headers {
                frameOptions {
                    sameOrigin = true
                }
            }
        }
    }
}

如果不希望添加默认值,并且希望对应该使用的内容进行显式控制,则可以禁用默认值。下面举例说明:

如果你使用 Spring Security的配置,以下将只添加缓存控制

例2.自定义缓存控制头

Java

@EnableWebSecurity
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http
			// ...
			.headers(headers -> headers
				// do not use any default headers unless explicitly listed
				.defaultsDisabled()
				.cacheControl(withDefaults())
			);
	}
}

XML

<http>
	<!-- ... -->

	<headers defaults-disabled="true">
		<cache-control/>
	</headers>
</http>

Kotlin

@EnableWebSecurity
class SecurityConfig : WebSecurityConfigurerAdapter() {
    override fun configure(http: HttpSecurity) {
        http {
            // ...
            headers {
                // do not use any default headers unless explicitly listed
                defaultsDisabled = true
                cacheControl {
                }
            }
        }
    }
}

如果有必要,可以通过以下配置禁用所有HTTP安全响应头:

例3.禁用所有HTTP安全标头

Java

@EnableWebSecurity
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http
			// ...
			.headers(headers -> headers.disable());
	}
}

XML

<http>
	<!-- ... -->

	<headers disabled="true" />
</http>

Kotlin

@EnableWebSecurity
class SecurityConfig : WebSecurityConfigurerAdapter() {
    override fun configure(http: HttpSecurity) {
        http {
            // ...
            headers {
                disable()
            }
        }
    }
}

# 缓存控制

Spring 默认情况下,安全性包括缓存控制标头。

但是,如果你实际上想要缓存特定的响应,那么你的应用程序可以有选择地调用[HttpServletResponse.setheader(字符串,字符串)](https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletResponse.html#setHeader(java.lang.String,java.lang.String))来覆盖由 Spring Security设置的头。这对于确保CSS、JavaScript和图像等内容被适当地缓存是有用的。

当使用 Spring Web MVC时,这通常是在你的配置中完成的。关于如何做到这一点的详细信息可以在 Spring 参考文档的静态资源 (opens new window)部分中找到。

如果有必要,还可以禁用 Spring Security的缓存控制HTTP响应头。

例4.已禁用缓存控制

Java

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {

	@Override
	protected void configure(HttpSecurity http) {
		http
			// ...
			.headers(headers -> headers
				.cacheControl(cache -> cache.disable())
			);
	}
}

XML

<http>
	<!-- ... -->

	<headers>
		<cache-control disabled="true"/>
	</headers>
</http>

Kotlin

@EnableWebSecurity
class SecurityConfig : WebSecurityConfigurerAdapter() {

    override fun configure(http: HttpSecurity) {
       http {
            headers {
                cacheControl {
                    disable()
                }
            }
        }
    }
}

# 内容类型选项

Spring 默认情况下,安全性包括内容类型标头。但是,你可以通过以下方式禁用它:

例5.禁用内容类型选项

Java

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends
		WebSecurityConfigurerAdapter {

	@Override
	protected void configure(HttpSecurity http) {
		http
			// ...
			.headers(headers -> headers
				.contentTypeOptions(contentTypeOptions -> contentTypeOptions.disable())
			);
	}
}

XML

<http>
	<!-- ... -->

	<headers>
		<content-type-options disabled="true"/>
	</headers>
</http>

Kotlin

@EnableWebSecurity
class SecurityConfig : WebSecurityConfigurerAdapter() {

    override fun configure(http: HttpSecurity) {
       http {
            headers {
                contentTypeOptions {
                    disable()
                }
            }
        }
    }
}

#

Spring 默认情况下,Security提供严格的运输安全头。但是,你可以显式地定制结果。例如,下面是一个显式提供HSTS的示例:

例6.严格的运输安全

Java

@EnableWebSecurity
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http
			// ...
			.headers(headers -> headers
				.httpStrictTransportSecurity(hsts -> hsts
					.includeSubDomains(true)
					.preload(true)
					.maxAgeInSeconds(31536000)
				)
			);
	}
}

XML

<http>
	<!-- ... -->

	<headers>
		<hsts
			include-subdomains="true"
			max-age-seconds="31536000"
			preload="true" />
	</headers>
</http>

Kotlin

@EnableWebSecurity
class SecurityConfig : WebSecurityConfigurerAdapter() {

    override fun configure(http: HttpSecurity) {
        http {
            headers {
                httpStrictTransportSecurity {
                    includeSubDomains = true
                    preload = true
                    maxAgeInSeconds = 31536000
                }
            }
        }
    }
}

#

出于被动的原因, Spring Security为HTTP公钥固定提供了 Servlet 支持,但它是不再推荐

你可以通过以下配置启用HPKP头:

例7. HTTP公钥固定

Java

@EnableWebSecurity
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {

	@Override
	protected void configure(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=")
				)
			);
	}
}

XML

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

Kotlin

@EnableWebSecurity
class SecurityConfig : WebSecurityConfigurerAdapter() {

    override fun configure(http: HttpSecurity) {
        http {
            headers {
                httpPublicKeyPinning {
                    includeSubDomains = true
                    reportUri = "https://example.net/pkp-report"
                    pins = mapOf("d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=" to "sha256",
                            "E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=" to "sha256")
                }
            }
        }
    }
}

# X-帧-选项

默认情况下, Spring Security使用X-帧-选项禁用在iFrame中的呈现。

你可以使用以下方法自定义框架选项,以便在配置中使用相同的原点:

例8. x-frame-options:SameOrigin

Java

@EnableWebSecurity
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http
			// ...
			.headers(headers -> headers
				.frameOptions(frameOptions -> frameOptions
					.sameOrigin()
				)
			);
	}
}

XML

<http>
	<!-- ... -->

	<headers>
		<frame-options
		policy="SAMEORIGIN" />
	</headers>
</http>

Kotlin

@EnableWebSecurity
class SecurityConfig : WebSecurityConfigurerAdapter() {

    override fun configure(http: HttpSecurity) {
        http {
            headers {
                frameOptions {
                    sameOrigin = true
                }
            }
        }
    }
}

# X-XSS-保护

默认情况下, Spring Security指示浏览器使用\<headers-xss-protection,X-XSS-Protection header>来阻止反射的XSS攻击。但是,你可以更改此默认值。例如,以下配置指定 Spring 安全性不应再指示浏览器阻止内容:

例9. X-XSS-保护定制

Java

@EnableWebSecurity
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http
			// ...
			.headers(headers -> headers
				.xssProtection(xss -> xss
					.block(false)
				)
			);
	}
}

XML

<http>
	<!-- ... -->

	<headers>
		<xss-protection block="false"/>
	</headers>
</http>

Kotlin

@EnableWebSecurity
class SecurityConfig : WebSecurityConfigurerAdapter() {

    override fun configure(http: HttpSecurity) {
        // ...
        http {
            headers {
                xssProtection {
                    block = false
                }
            }
        }
    }
}

#

Spring 默认情况下安全性不添加内容安全策略,因为合理的默认情况是不可能在没有上下文的情况下知道应用程序的。Web应用程序作者必须声明安全策略,以强制执行和/或监视受保护的资源。

例如,给出以下安全策略:

例10.内容安全策略示例

Content-Security-Policy: script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/

你可以启用CSP报头,如下所示:

例11.内容安全策略

Java

@EnableWebSecurity
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {

	@Override
	protected void configure(HttpSecurity http) {
		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/")
				)
			);
	}
}

XML

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

Kotlin

@EnableWebSecurity
class SecurityConfig : WebSecurityConfigurerAdapter() {

    override fun configure(http: HttpSecurity) {
        http {
            // ...
            headers {
                contentSecurityPolicy {
                    policyDirectives = "script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/"
                }
            }
        }
    }
}

要启用CSPreport-only头,请提供以下配置:

例12.仅提供内容安全策略报告

Java

@EnableWebSecurity
public class WebSecurityConfig extends
		WebSecurityConfigurerAdapter {

	@Override
	protected void configure(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()
				)
			);
	}
}

XML

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

Kotlin

@EnableWebSecurity
class SecurityConfig : WebSecurityConfigurerAdapter() {

    override fun configure(http: HttpSecurity) {
        http {
            // ...
            headers {
                contentSecurityPolicy {
                    policyDirectives = "script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/"
                    reportOnly = true
                }
            }
        }
    }
}

# 推荐人政策

Spring 默认情况下,安全性不添加推荐人政策标头。你可以使用如下所示的配置启用Referrer策略标头:

例13.推荐人政策

Java

@EnableWebSecurity
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {

	@Override
	protected void configure(HttpSecurity http) {
		http
			// ...
			.headers(headers -> headers
				.referrerPolicy(referrer -> referrer
					.policy(ReferrerPolicy.SAME_ORIGIN)
				)
			);
	}
}

XML

<http>
	<!-- ... -->

	<headers>
		<referrer-policy policy="same-origin" />
	</headers>
</http>

Kotlin

@EnableWebSecurity
class SecurityConfig : WebSecurityConfigurerAdapter() {

    override fun configure(http: HttpSecurity) {
        http {
            // ...
            headers {
                referrerPolicy {
                    policy = ReferrerPolicy.SAME_ORIGIN
                }
            }
        }
    }
}

# 特征策略

Spring 默认情况下,安全性不添加特征策略头。以下Feature-Policy标题:

例14.功能策略示例

Feature-Policy: geolocation 'self'

可以使用如下所示的配置启用功能策略标头:

例15.特征-策略

Java

@EnableWebSecurity
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http
			// ...
			.headers(headers -> headers
				.featurePolicy("geolocation 'self'")
			);
	}
}

XML

<http>
	<!-- ... -->

	<headers>
		<feature-policy policy-directives="geolocation 'self'" />
	</headers>
</http>

Kotlin

@EnableWebSecurity
class SecurityConfig : WebSecurityConfigurerAdapter() {

    override fun configure(http: HttpSecurity) {
        http {
            // ...
            headers {
                featurePolicy("geolocation 'self'")
            }
        }
    }
}

# 权限策略

Spring 默认情况下,安全性不添加权限策略标头。以下Permissions-Policy标题:

例16.权限-策略示例

Permissions-Policy: geolocation=(self)

可以使用如下所示的配置启用权限策略标头:

例17.权限-策略

Java

@EnableWebSecurity
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http
			// ...
			.headers(headers -> headers
				.permissionsPolicy(permissions -> permissions
					.policy("geolocation=(self)")
				)
			);
	}
}

XML

<http>
	<!-- ... -->

	<headers>
		<permissions-policy policy="geolocation=(self)" />
	</headers>
</http>

Kotlin

@EnableWebSecurity
class SecurityConfig : WebSecurityConfigurerAdapter() {

    override fun configure(http: HttpSecurity) {
        http {
            // ...
            headers {
                permissionPolicy {
                    policy = "geolocation=(self)"
                }
            }
        }
    }
}

# 清除站点数据

Spring 默认情况下,安全性不添加清除站点数据头。以下Clear-Site-Data报头:

例18.清除站点数据示例

Clear-Site-Data: "cache", "cookies"

可以通过以下配置在注销时发送:

例19.清除站点数据

Java

@EnableWebSecurity
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http
			// ...
			.logout()
				.addLogoutHandler(new HeaderWriterLogoutHandler(new ClearSiteDataHeaderWriter(CACHE, COOKIES)));
	}
}

Kotlin

@EnableWebSecurity
class SecurityConfig : WebSecurityConfigurerAdapter() {

    override fun configure(http: HttpSecurity) {
        http {
            // ...
            logout {
                addLogoutHandler(HeaderWriterLogoutHandler(ClearSiteDataHeaderWriter(CACHE, COOKIES)))
            }
        }
    }
}

# 自定义标头

Spring 安全性有一些机制,以使其方便地将更常见的安全性标题添加到你的应用程序中。然而,它也提供了钩子来支持添加自定义头。

# 静态标头

有时,你可能希望将自定义的安全标头插入到你的应用程序中,而这些标头不受开箱即用的支持。例如,给出以下自定义安全标头:

X-Custom-Security-Header: header-value

可以使用以下配置将标头添加到响应中:

例20. Staticheaderswriter

Java

@EnableWebSecurity
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http
			// ...
			.headers(headers -> headers
				.addHeaderWriter(new StaticHeadersWriter("X-Custom-Security-Header","header-value"))
			);
	}
}

XML

<http>
	<!-- ... -->

	<headers>
		<header name="X-Custom-Security-Header" value="header-value"/>
	</headers>
</http>

Kotlin

@EnableWebSecurity
class SecurityConfig : WebSecurityConfigurerAdapter() {

    override fun configure(http: HttpSecurity) {
        http {
            // ...
            headers {
                addHeaderWriter(StaticHeadersWriter("X-Custom-Security-Header","header-value"))
            }
        }
    }
}

# Headers Writer

当名称空间或 Java 配置不支持所需的标题时,可以创建自定义HeadersWriter实例,甚至提供HeadersWriter的自定义实现。

让我们来看一个使用XFrameOptionsHeaderWriter自定义实例的示例。如果要显式配置X-帧-选项,可以通过以下配置完成:

例21. Headers Writer

Java

@EnableWebSecurity
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http
			// ...
			.headers(headers -> headers
				.addHeaderWriter(new XFrameOptionsHeaderWriter(XFrameOptionsMode.SAMEORIGIN))
			);
	}
}

XML

<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"/>

Kotlin

@EnableWebSecurity
class SecurityConfig : WebSecurityConfigurerAdapter() {

    override fun configure(http: HttpSecurity) {
        http {
            // ...
            headers {
                addHeaderWriter(XFrameOptionsHeaderWriter(XFrameOptionsMode.SAMEORIGIN))
            }
        }
    }
}

# 委托请求者headerwriter

有时,你可能只想为某些请求写一个头。例如,你可能只想保护你的登录页面不被框起来。你可以使用DelegatingRequestMatcherHeaderWriter来执行此操作。

在 Java 配置中使用DelegatingRequestMatcherHeaderWriter的示例如下所示:

例22.授权请求MatcherHeaderWriter Java 配置

Java

@EnableWebSecurity
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		RequestMatcher matcher = new AntPathRequestMatcher("/login");
		DelegatingRequestMatcherHeaderWriter headerWriter =
			new DelegatingRequestMatcherHeaderWriter(matcher,new XFrameOptionsHeaderWriter());
		http
			// ...
			.headers(headers -> headers
				.frameOptions(frameOptions -> frameOptions.disable())
				.addHeaderWriter(headerWriter)
			);
	}
}

XML

<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.web.util.matcher.AntPathRequestMatcher"
			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>

Kotlin

@EnableWebSecurity
class SecurityConfig : WebSecurityConfigurerAdapter() {

    override fun configure(http: HttpSecurity) {
        val matcher: RequestMatcher = AntPathRequestMatcher("/login")
        val headerWriter = DelegatingRequestMatcherHeaderWriter(matcher, XFrameOptionsHeaderWriter())
       http {
            headers {
                frameOptions {
                    disable()
                }
                addHeaderWriter(headerWriter)
            }
        }
    }
}

Cross Site Request Forgery (CSRF) for Servlet EnvironmentsHTTP