Make X-Xss-Protection configurable through ServerHttpSecurity

OWASP recommends using "X-Xss-Protection: 0". The default is currently
"X-Xss-Protection: 1; mode=block". In 6.0, the default will be "0".

This commits adds the ability to configure the xssProtection header
value in ServerHttpSecurity.

This commit deprecates the use of "enabled" and "block" booleans to
configure XSS protection, as the state "!enabled + block" is invalid.
This impacts HttpSecurity.

Issue gh-9631
This commit is contained in:
Daniel Garnier-Moiroux 2022-09-27 14:56:00 +02:00 committed by Steve Riesenberg
parent 7b1158ddb7
commit 93250013e4
No known key found for this signature in database
GPG Key ID: 5F311AB48A55D521
13 changed files with 439 additions and 56 deletions

View File

@ -729,7 +729,10 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
* If false, will not specify the mode as blocked. In this instance, any content
* will be attempted to be fixed. If true, the content will be replaced with "#".
* @param enabled the new value
* @deprecated use
* {@link XXssConfig#headerValue(XXssProtectionHeaderWriter.HeaderValue)} instead
*/
@Deprecated
public XXssConfig block(boolean enabled) {
this.writer.setBlock(enabled);
return this;
@ -757,12 +760,49 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
* X-XSS-Protection: 0
* </pre>
* @param enabled the new value
* @deprecated use
* {@link XXssConfig#headerValue(XXssProtectionHeaderWriter.HeaderValue)} instead
*/
@Deprecated
public XXssConfig xssProtectionEnabled(boolean enabled) {
this.writer.setEnabled(enabled);
return this;
}
/**
* Sets the value of the X-XSS-PROTECTION header. OWASP recommends using
* {@link XXssProtectionHeaderWriter.HeaderValue#DISABLED}.
*
* If {@link XXssProtectionHeaderWriter.HeaderValue#DISABLED}, will specify that
* X-XSS-Protection is disabled. For example:
*
* <pre>
* X-XSS-Protection: 0
* </pre>
*
* If {@link XXssProtectionHeaderWriter.HeaderValue#ENABLED}, will contain a value
* of 1, but will not specify the mode as blocked. In this instance, any content
* will be attempted to be fixed. For example:
*
* <pre>
* X-XSS-Protection: 1
* </pre>
*
* If {@link XXssProtectionHeaderWriter.HeaderValue#ENABLED_MODE_BLOCK}, will
* contain a value of 1 and will specify mode as blocked. The content will be
* replaced with "#". For example:
*
* <pre>
* X-XSS-Protection: 1 ; mode=block
* </pre>
* @param headerValue the new header value
* @since 5.8
*/
public XXssConfig headerValue(XXssProtectionHeaderWriter.HeaderValue headerValue) {
this.writer.setHeaderValue(headerValue);
return this;
}
/**
* Disables X-XSS-Protection header (does not include it)
* @return the {@link HeadersConfigurer} for additional configuration

View File

@ -2859,6 +2859,18 @@ public class ServerHttpSecurity {
return HeaderSpec.this;
}
/**
* Sets the value of x-xss-protection header. OWASP recommends using
* {@link XXssProtectionServerHttpHeadersWriter.HeaderValue#DISABLED}.
* @param headerValue the headerValue
* @return the {@link HeaderSpec} to continue configuring
* @since 5.8
*/
public HeaderSpec headerValue(XXssProtectionServerHttpHeadersWriter.HeaderValue headerValue) {
HeaderSpec.this.xss.setHeaderValue(headerValue);
return HeaderSpec.this;
}
}
/**

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,16 +16,21 @@
package org.springframework.security.config.web.server
import org.springframework.security.web.server.header.XXssProtectionServerHttpHeadersWriter.HeaderValue
/**
* A Kotlin DSL to configure the [ServerHttpSecurity] XSS protection header using
* idiomatic Kotlin code.
*
* @property headerValue the value of the X-XSS-Protection header. OWASP recommends [HeaderValue.DISABLED].
*
* @author Eleftheria Stein
* @since 5.4
*/
@ServerSecurityMarker
class ServerXssProtectionDsl {
private var disabled = false
var headerValue: HeaderValue? = null
/**
* Disables cache control response headers
@ -36,6 +41,7 @@ class ServerXssProtectionDsl {
internal fun get(): (ServerHttpSecurity.HeaderSpec.XssProtectionSpec) -> Unit {
return { xss ->
headerValue?.also { xss.headerValue(headerValue) }
if (disabled) {
xss.disable()
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -18,6 +18,7 @@ package org.springframework.security.config.web.servlet.headers
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer
import org.springframework.security.web.header.writers.XXssProtectionHeaderWriter.HeaderValue
/**
* A Kotlin DSL to configure the [HttpSecurity] XSS protection header using
@ -28,11 +29,15 @@ import org.springframework.security.config.annotation.web.configurers.HeadersCon
* @property block whether to specify the mode as blocked
* @property xssProtectionEnabled if true, the header value will contain a value of 1.
* If false, will explicitly disable specify that X-XSS-Protection is disabled.
* @property headerValue the value of the X-XSS-Protection header. OWASP recommends [HeaderValue.DISABLED].
*/
@HeadersSecurityMarker
class XssProtectionConfigDsl {
@Deprecated("use headerValue instead")
var block: Boolean? = null
@Deprecated("use headerValue instead")
var xssProtectionEnabled: Boolean? = null
var headerValue: HeaderValue? = null
private var disabled = false
@ -47,6 +52,7 @@ class XssProtectionConfigDsl {
return { xssProtection ->
block?.also { xssProtection.block(block!!) }
xssProtectionEnabled?.also { xssProtection.xssProtectionEnabled(xssProtectionEnabled!!) }
headerValue?.also { xssProtection.headerValue(headerValue) }
if (disabled) {
xssProtection.disable()

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -37,6 +37,7 @@ import org.springframework.security.web.header.writers.CrossOriginEmbedderPolicy
import org.springframework.security.web.header.writers.CrossOriginOpenerPolicyHeaderWriter;
import org.springframework.security.web.header.writers.CrossOriginResourcePolicyHeaderWriter;
import org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter.ReferrerPolicy;
import org.springframework.security.web.header.writers.XXssProtectionHeaderWriter;
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter.XFrameOptionsMode;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
@ -58,6 +59,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
* @author Vedran Pavic
* @author Eleftheria Stein
* @author Marcus Da Coregio
* @author Daniel Garnier-Moiroux
*/
@ExtendWith(SpringTestContextExtension.class)
public class HeadersConfigurerTests {
@ -171,6 +173,15 @@ public class HeadersConfigurerTests {
assertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.X_XSS_PROTECTION);
}
@Test
public void getWhenHeaderDefaultsDisabledAndXssProtectionConfiguredValueDisabledThenOnlyXssProtectionHeaderInResponse()
throws Exception {
this.spring.register(XssProtectionValueDisabledConfig.class).autowire();
MvcResult mvcResult = this.mvc.perform(get("/").secure(true))
.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "0")).andReturn();
assertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.X_XSS_PROTECTION);
}
@Test
public void getWhenOnlyXssProtectionConfiguredInLambdaThenOnlyXssProtectionHeaderInResponse() throws Exception {
this.spring.register(XssProtectionInLambdaConfig.class).autowire();
@ -179,6 +190,15 @@ public class HeadersConfigurerTests {
assertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.X_XSS_PROTECTION);
}
@Test
public void getWhenHeaderDefaultsDisabledAndXssProtectionConfiguredValueDisabledInLambdaThenOnlyXssProtectionHeaderInResponse()
throws Exception {
this.spring.register(XssProtectionValueDisabledInLambdaConfig.class).autowire();
MvcResult mvcResult = this.mvc.perform(get("/").secure(true))
.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "0")).andReturn();
assertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.X_XSS_PROTECTION);
}
@Test
public void getWhenFrameOptionsSameOriginConfiguredThenFrameOptionsHeaderHasValueSameOrigin() throws Exception {
this.spring.register(HeadersCustomSameOriginConfig.class).autowire();
@ -679,6 +699,22 @@ public class HeadersConfigurerTests {
}
@EnableWebSecurity
static class XssProtectionValueDisabledConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.headers()
.defaultsDisabled()
.xssProtection()
.headerValue(XXssProtectionHeaderWriter.HeaderValue.DISABLED);
// @formatter:on
}
}
@EnableWebSecurity
static class XssProtectionInLambdaConfig extends WebSecurityConfigurerAdapter {
@ -696,6 +732,25 @@ public class HeadersConfigurerTests {
}
@EnableWebSecurity
static class XssProtectionValueDisabledInLambdaConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.headers((headers) ->
headers
.defaultsDisabled()
.xssProtection((xXssConfig) ->
xXssConfig.headerValue(XXssProtectionHeaderWriter.HeaderValue.DISABLED)
)
);
// @formatter:on
}
}
@EnableWebSecurity
static class HeadersCustomSameOriginConfig extends WebSecurityConfigurerAdapter {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -31,6 +31,7 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.web.header.writers.StaticHeadersWriter;
import org.springframework.security.web.header.writers.XXssProtectionHeaderWriter;
import org.springframework.security.web.header.writers.frameoptions.StaticAllowFromStrategy;
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter;
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
@ -273,8 +274,7 @@ public class NamespaceHttpHeadersTests {
// xss-protection@enabled and xss-protection@block
.defaultsDisabled()
.xssProtection()
.xssProtectionEnabled(true)
.block(false);
.headerValue(XXssProtectionHeaderWriter.HeaderValue.ENABLED);
// @formatter:on
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -296,6 +296,51 @@ public class HeaderSpecTests {
assertHeaders();
}
@Test
public void headersWhenXssProtectionValueDisabledThenXssProtectionWritten() {
this.expectedHeaders.set(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "0");
// @formatter:off
this.http.headers()
.xssProtection()
.headerValue(XXssProtectionServerHttpHeadersWriter.HeaderValue.DISABLED);
// @formatter:on
assertHeaders();
}
@Test
public void headersWhenXssProtectionValueEnabledThenXssProtectionWritten() {
this.expectedHeaders.set(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "1");
// @formatter:off
this.http.headers()
.xssProtection()
.headerValue(XXssProtectionServerHttpHeadersWriter.HeaderValue.ENABLED);
// @formatter:on
assertHeaders();
}
@Test
public void headersWhenXssProtectionValueEnabledModeBlockThenXssProtectionWritten() {
this.expectedHeaders.set(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "1 ; mode=block");
// @formatter:off
this.http.headers()
.xssProtection()
.headerValue(XXssProtectionServerHttpHeadersWriter.HeaderValue.ENABLED_MODE_BLOCK);
// @formatter:on
assertHeaders();
}
@Test
public void headersWhenXssProtectionValueDisabledInLambdaThenXssProtectionWritten() {
this.expectedHeaders.set(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "0");
// @formatter:off
this.http.headers()
.xssProtection((xssProtection) ->
xssProtection.headerValue(XXssProtectionServerHttpHeadersWriter.HeaderValue.DISABLED)
);
// @formatter:on
assertHeaders();
}
@Test
public void headersWhenFeaturePolicyEnabledThenFeaturePolicyWritten() {
String policyDirectives = "Feature-Policy";

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -96,4 +96,29 @@ class ServerXssProtectionDslTests {
}
}
}
@Test
fun `request when xss protection value disabled then xss header in response`() {
this.spring.register(XssValueDisabledConfig::class.java).autowire()
this.client.get()
.uri("/")
.exchange()
.expectHeader().valueEquals(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "0")
}
@EnableWebFluxSecurity
@EnableWebFlux
open class XssValueDisabledConfig {
@Bean
open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
headers {
xssProtection {
headerValue = XXssProtectionServerHttpHeadersWriter.HeaderValue.DISABLED
}
}
}
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,12 +19,16 @@ package org.springframework.security.config.web.servlet.headers
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
import org.springframework.security.config.web.servlet.invoke
import org.springframework.security.config.test.SpringTestContext
import org.springframework.security.config.test.SpringTestContextExtension
import org.springframework.security.web.SecurityFilterChain
import org.springframework.security.web.header.writers.XXssProtectionHeaderWriter
import org.springframework.security.web.server.header.XXssProtectionServerHttpHeadersWriter
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.get
@ -138,4 +142,32 @@ class XssProtectionConfigDslTests {
}
}
}
@Test
fun `headers when XSS protection header value disabled then X-XSS-Protection header is 0`() {
this.spring.register(XssProtectionHeaderValueDisabledFunctionConfig::class.java).autowire()
this.mockMvc.get("/") {
secure = true
}.andExpect {
header { string(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "0") }
}
}
@Configuration
@EnableWebSecurity
open class XssProtectionHeaderValueDisabledFunctionConfig () {
@Bean
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
headers {
defaultsDisabled = true
xssProtection {
headerValue = XXssProtectionHeaderWriter.HeaderValue.DISABLED
}
}
}
return http.build()
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -20,6 +20,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.web.header.HeaderWriter;
import org.springframework.util.Assert;
/**
* Renders the <a href=
@ -34,25 +35,19 @@ public final class XXssProtectionHeaderWriter implements HeaderWriter {
private static final String XSS_PROTECTION_HEADER = "X-XSS-Protection";
private boolean enabled;
private boolean block;
private String headerValue;
private HeaderValue headerValue;
/**
* Create a new instance
*/
public XXssProtectionHeaderWriter() {
this.enabled = true;
this.block = true;
updateHeaderValue();
this.headerValue = HeaderValue.ENABLED_MODE_BLOCK;
}
@Override
public void writeHeaders(HttpServletRequest request, HttpServletResponse response) {
if (!response.containsHeader(XSS_PROTECTION_HEADER)) {
response.setHeader(XSS_PROTECTION_HEADER, this.headerValue);
response.setHeader(XSS_PROTECTION_HEADER, this.headerValue.toString());
}
}
@ -77,37 +72,88 @@ public final class XXssProtectionHeaderWriter implements HeaderWriter {
* X-XSS-Protection: 0
* </pre>
* @param enabled the new value
* @deprecated use {@link XXssProtectionHeaderWriter#setHeaderValue(HeaderValue)}
* instead
*/
@Deprecated
public void setEnabled(boolean enabled) {
if (!enabled) {
setBlock(false);
this.headerValue = HeaderValue.DISABLED;
}
else if (this.headerValue == HeaderValue.DISABLED) {
this.headerValue = HeaderValue.ENABLED;
}
this.enabled = enabled;
updateHeaderValue();
}
/**
* If false, will not specify the mode as blocked. In this instance, any content will
* be attempted to be fixed. If true, the content will be replaced with "#".
* @param block the new value
* @deprecated use {@link XXssProtectionHeaderWriter#setHeaderValue(HeaderValue)}
* instead
*/
@Deprecated
public void setBlock(boolean block) {
if (!this.enabled && block) {
if (this.headerValue == HeaderValue.DISABLED && block) {
throw new IllegalArgumentException("Cannot set block to true with enabled false");
}
this.block = block;
updateHeaderValue();
this.headerValue = block ? HeaderValue.ENABLED_MODE_BLOCK : HeaderValue.ENABLED;
}
private void updateHeaderValue() {
if (!this.enabled) {
this.headerValue = "0";
return;
/**
* Sets the value of the X-XSS-PROTECTION header.
* <p>
* If {@link HeaderValue#DISABLED}, will specify that X-XSS-Protection is disabled.
* For example:
*
* <pre>
* X-XSS-Protection: 0
* </pre>
* <p>
* If {@link HeaderValue#ENABLED}, will contain a value of 1, but will not specify the
* mode as blocked. In this instance, any content will be attempted to be fixed. For
* example:
*
* <pre>
* X-XSS-Protection: 1
* </pre>
* <p>
* If {@link HeaderValue#ENABLED_MODE_BLOCK}, will contain a value of 1 and will
* specify mode as blocked. The content will be replaced with "#". For example:
*
* <pre>
* X-XSS-Protection: 1 ; mode=block
* </pre>
* @param headerValue the new header value
* @throws IllegalArgumentException when headerValue is null
* @since 5.8
*/
public void setHeaderValue(HeaderValue headerValue) {
Assert.notNull(headerValue, "headerValue cannot be null");
this.headerValue = headerValue;
}
/**
* The value of the x-xss-protection header. One of: "0", "1", "1 ; mode=block"
*
* @author Daniel Garnier-Moiroux
* @since 5.8
*/
public enum HeaderValue {
DISABLED("0"), ENABLED("1"), ENABLED_MODE_BLOCK("1; mode=block");
private final String value;
HeaderValue(String value) {
this.value = value;
}
this.headerValue = "1";
if (this.block) {
this.headerValue += "; mode=block";
@Override
public String toString() {
return this.value;
}
}
@Override

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -26,24 +26,22 @@ import org.springframework.web.server.ServerWebExchange;
* Add the x-xss-protection header.
*
* @author Rob Winch
* @author Daniel Garnier-Moiroux
* @since 5.0
*/
public class XXssProtectionServerHttpHeadersWriter implements ServerHttpHeadersWriter {
public static final String X_XSS_PROTECTION = "X-XSS-Protection";
private boolean enabled;
private boolean block;
private ServerHttpHeadersWriter delegate;
private HeaderValue headerValue;
/**
* Creates a new instance
*/
public XXssProtectionServerHttpHeadersWriter() {
this.enabled = true;
this.block = true;
this.headerValue = HeaderValue.ENABLED_MODE_BLOCK;
updateDelegate();
}
@ -73,12 +71,17 @@ public class XXssProtectionServerHttpHeadersWriter implements ServerHttpHeadersW
* X-XSS-Protection: 0
* </pre>
* @param enabled the new value
* @deprecated use
* {@link XXssProtectionServerHttpHeadersWriter#setHeaderValue(HeaderValue)} instead
*/
@Deprecated
public void setEnabled(boolean enabled) {
if (!enabled) {
setBlock(false);
this.headerValue = HeaderValue.DISABLED;
}
else if (this.headerValue == HeaderValue.DISABLED) {
this.headerValue = HeaderValue.ENABLED;
}
this.enabled = enabled;
updateDelegate();
}
@ -86,27 +89,78 @@ public class XXssProtectionServerHttpHeadersWriter implements ServerHttpHeadersW
* If false, will not specify the mode as blocked. In this instance, any content will
* be attempted to be fixed. If true, the content will be replaced with "#".
* @param block the new value
* @deprecated use
* {@link XXssProtectionServerHttpHeadersWriter#setHeaderValue(HeaderValue)} instead
*/
@Deprecated
public void setBlock(boolean block) {
Assert.isTrue(this.enabled || !block, "Cannot set block to true with enabled false");
this.block = block;
Assert.isTrue(this.headerValue != HeaderValue.DISABLED || !block,
"Cannot set block to true with enabled false");
this.headerValue = block ? HeaderValue.ENABLED_MODE_BLOCK : HeaderValue.ENABLED;
updateDelegate();
}
/**
* Sets the value of the X-XSS-PROTECTION header.
* <p>
* If {@link HeaderValue#DISABLED}, will specify that X-XSS-Protection is disabled.
* For example:
*
* <pre>
* X-XSS-Protection: 0
* </pre>
* <p>
* If {@link HeaderValue#ENABLED}, will contain a value of 1, but will not specify the
* mode as blocked. In this instance, any content will be attempted to be fixed. For
* example:
*
* <pre>
* X-XSS-Protection: 1
* </pre>
* <p>
* If {@link HeaderValue#ENABLED_MODE_BLOCK}, will contain a value of 1 and will
* specify mode as blocked. The content will be replaced with "#". For example:
*
* <pre>
* X-XSS-Protection: 1 ; mode=block
* </pre>
* @param headerValue the new headerValue
* @throws IllegalArgumentException if headerValue is null
* @since 5.8
*/
public void setHeaderValue(HeaderValue headerValue) {
Assert.notNull(headerValue, "headerValue cannot be null");
this.headerValue = headerValue;
updateDelegate();
}
/**
* The value of the x-xss-protection header. One of: "0", "1", "1 ; mode=block"
*
* @author Daniel Garnier-Moiroux
* @since 5.8
*/
public enum HeaderValue {
DISABLED("0"), ENABLED("1"), ENABLED_MODE_BLOCK("1 ; mode=block");
private final String value;
HeaderValue(String value) {
this.value = value;
}
@Override
public String toString() {
return this.value;
}
}
private void updateDelegate() {
Builder builder = StaticServerHttpHeadersWriter.builder();
builder.header(X_XSS_PROTECTION, createHeaderValue());
builder.header(X_XSS_PROTECTION, this.headerValue.toString());
this.delegate = builder.build();
}
private String createHeaderValue() {
if (!this.enabled) {
return "0";
}
if (!this.block) {
return "1";
}
return "1 ; mode=block";
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -94,4 +94,34 @@ public class XXssProtectionHeaderWriterTests {
assertThat(this.response.getHeader(XSS_PROTECTION_HEADER)).isSameAs(value);
}
@Test
void writeHeaderWhenDisabled() {
this.writer.setHeaderValue(XXssProtectionHeaderWriter.HeaderValue.DISABLED);
this.writer.writeHeaders(this.request, this.response);
assertThat(this.response.getHeaderNames()).hasSize(1);
assertThat(this.response.getHeaderValues("X-XSS-Protection")).containsOnly("0");
}
@Test
void writeHeaderWhenEnabled() {
this.writer.setHeaderValue(XXssProtectionHeaderWriter.HeaderValue.ENABLED);
this.writer.writeHeaders(this.request, this.response);
assertThat(this.response.getHeaderNames()).hasSize(1);
assertThat(this.response.getHeaderValues("X-XSS-Protection")).containsOnly("1");
}
@Test
void writeHeaderWhenEnabledModeBlock() {
this.writer.setHeaderValue(XXssProtectionHeaderWriter.HeaderValue.ENABLED_MODE_BLOCK);
this.writer.writeHeaders(this.request, this.response);
assertThat(this.response.getHeaderNames()).hasSize(1);
assertThat(this.response.getHeaderValues("X-XSS-Protection")).containsOnly("1; mode=block");
}
@Test
public void setHeaderValueNullThenThrowsIllegalArgumentException() {
assertThatIllegalArgumentException().isThrownBy(() -> this.writer.setHeaderValue(null))
.withMessage("headerValue cannot be null");
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -24,6 +24,7 @@ import org.springframework.mock.web.server.MockServerWebExchange;
import org.springframework.web.server.ServerWebExchange;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/**
* @author Rob Winch
@ -37,6 +38,12 @@ public class XXssProtectionServerHttpHeadersWriterTests {
XXssProtectionServerHttpHeadersWriter writer = new XXssProtectionServerHttpHeadersWriter();
@Test
void setHeaderValueNullThenThrowsIllegalArgumentException() {
assertThatIllegalArgumentException().isThrownBy(() -> this.writer.setHeaderValue(null))
.withMessage("headerValue cannot be null");
}
@Test
public void writeHeadersWhenNoHeadersThenWriteHeaders() {
this.writer.writeHttpHeaders(this.exchange);
@ -70,4 +77,29 @@ public class XXssProtectionServerHttpHeadersWriterTests {
assertThat(this.headers.get(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION)).containsOnly(headerValue);
}
@Test
void writeHeadersWhenDisabledThenWriteHeaders() {
this.writer.setHeaderValue(XXssProtectionServerHttpHeadersWriter.HeaderValue.DISABLED);
this.writer.writeHttpHeaders(this.exchange);
assertThat(this.headers).hasSize(1);
assertThat(this.headers.get(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION)).containsOnly("0");
}
@Test
void writeHeadersWhenEnabledThenWriteHeaders() {
this.writer.setHeaderValue(XXssProtectionServerHttpHeadersWriter.HeaderValue.ENABLED);
this.writer.writeHttpHeaders(this.exchange);
assertThat(this.headers).hasSize(1);
assertThat(this.headers.get(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION)).containsOnly("1");
}
@Test
void writeHeadersWhenEnabledModeBlockThenWriteHeaders() {
this.writer.setHeaderValue(XXssProtectionServerHttpHeadersWriter.HeaderValue.ENABLED_MODE_BLOCK);
this.writer.writeHttpHeaders(this.exchange);
assertThat(this.headers).hasSize(1);
assertThat(this.headers.get(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION))
.containsOnly("1 ; mode=block");
}
}