Browse Source

Default X-Xss-Protection header value to "0"

Closes gh-9631
pull/11986/head
Daniel Garnier-Moiroux 3 years ago committed by Steve Riesenberg
parent
commit
27059ced87
No known key found for this signature in database
GPG Key ID: 5F311AB48A55D521
  1. 47
      config/src/main/java/org/springframework/security/config/annotation/web/configurers/HeadersConfigurer.java
  2. 18
      config/src/main/java/org/springframework/security/config/http/HeadersBeanDefinitionParser.java
  3. 4
      config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java
  4. 10
      config/src/main/kotlin/org/springframework/security/config/annotation/web/headers/XssProtectionConfigDsl.kt
  5. 8
      config/src/main/resources/org/springframework/security/config/spring-security-6.0.rnc
  6. 16
      config/src/main/resources/org/springframework/security/config/spring-security-6.0.xsd
  7. 2
      config/src/test/java/org/springframework/security/config/annotation/web/configuration/HttpSecurityConfigurationTests.java
  8. 2
      config/src/test/java/org/springframework/security/config/annotation/web/configurers/HeadersConfigurerEagerHeadersTests.java
  9. 28
      config/src/test/java/org/springframework/security/config/annotation/web/configurers/HeadersConfigurerTests.java
  10. 6
      config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpHeadersTests.java
  11. 70
      config/src/test/java/org/springframework/security/config/http/HttpHeadersConfigTests.java
  12. 2
      config/src/test/java/org/springframework/security/config/web/server/HeaderSpecTests.java
  13. 2
      config/src/test/kotlin/org/springframework/security/config/annotation/web/HeadersDslTests.kt
  14. 2
      config/src/test/kotlin/org/springframework/security/config/annotation/web/HttpSecurityDslTests.kt
  15. 61
      config/src/test/kotlin/org/springframework/security/config/annotation/web/headers/XssProtectionConfigDslTests.kt
  16. 2
      config/src/test/kotlin/org/springframework/security/config/web/server/ServerHeadersDslTests.kt
  17. 2
      config/src/test/kotlin/org/springframework/security/config/web/server/ServerHttpSecurityDslTests.kt
  18. 6
      config/src/test/kotlin/org/springframework/security/config/web/server/ServerXssProtectionDslTests.kt
  19. 37
      config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithXssProtectionDisabled.xml
  20. 37
      config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithXssProtectionDisabledAndBlockSet.xml
  21. 37
      config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithXssProtectionDisabledAndHeaderValueOne.xml
  22. 37
      config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithXssProtectionEnabled.xml
  23. 37
      config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-XssProtectionDisabledAndEnabled.xml
  24. 37
      config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-XssProtectionDisabledSpecifyingBlock.xml
  25. 10
      docs/modules/ROOT/pages/features/exploits/headers.adoc
  26. 39
      docs/modules/ROOT/pages/reactive/exploits/headers.adoc
  27. 15
      docs/modules/ROOT/pages/servlet/appendix/namespace/http.adoc
  28. 11
      docs/modules/ROOT/pages/servlet/exploits/headers.adoc
  29. 52
      web/src/main/java/org/springframework/security/web/header/writers/XXssProtectionHeaderWriter.java
  30. 55
      web/src/main/java/org/springframework/security/web/server/header/XXssProtectionServerHttpHeadersWriter.java
  31. 33
      web/src/test/java/org/springframework/security/web/header/writers/XXssProtectionHeaderWriterTests.java
  32. 17
      web/src/test/java/org/springframework/security/web/server/header/XXssProtectionServerHttpHeadersWriterTests.java

47
config/src/main/java/org/springframework/security/config/annotation/web/configurers/HeadersConfigurer.java

@ -64,7 +64,7 @@ import org.springframework.util.Assert; @@ -64,7 +64,7 @@ import org.springframework.util.Assert;
* X-Content-Type-Options: nosniff
* Strict-Transport-Security: max-age=31536000 ; includeSubDomains
* X-Frame-Options: DENY
* X-XSS-Protection: 1; mode=block
* X-XSS-Protection: 0
* </pre>
*
* @author Rob Winch
@ -73,6 +73,7 @@ import org.springframework.util.Assert; @@ -73,6 +73,7 @@ import org.springframework.util.Assert;
* @author Eddú Meléndez
* @author Vedran Pavic
* @author Ankur Pathak
* @author Daniel Garnier-Moiroux
* @since 3.2
*/
public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
@ -733,50 +734,6 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>> @@ -733,50 +734,6 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
enable();
}
/**
* 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;
}
/**
* If true, the header value will contain a value of 1. For example:
*
* <pre>
* X-XSS-Protection: 1
* </pre>
*
* or if {@link XXssProtectionHeaderWriter#setBlock(boolean)} of the given
* {@link XXssProtectionHeaderWriter} is true
*
*
* <pre>
* X-XSS-Protection: 1; mode=block
* </pre>
*
* If false, will explicitly disable specify that X-XSS-Protection is disabled.
* For example:
*
* <pre>
* 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}.

18
config/src/main/java/org/springframework/security/config/http/HeadersBeanDefinitionParser.java

@ -69,10 +69,6 @@ public class HeadersBeanDefinitionParser implements BeanDefinitionParser { @@ -69,10 +69,6 @@ public class HeadersBeanDefinitionParser implements BeanDefinitionParser {
private static final String ATT_DISABLED = "disabled";
private static final String ATT_ENABLED = "enabled";
private static final String ATT_BLOCK = "block";
private static final String ATT_POLICY = "policy";
private static final String ATT_STRATEGY = "strategy";
@ -583,20 +579,6 @@ public class HeadersBeanDefinitionParser implements BeanDefinitionParser { @@ -583,20 +579,6 @@ public class HeadersBeanDefinitionParser implements BeanDefinitionParser {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(XXssProtectionHeaderWriter.class);
if (xssElt != null) {
boolean disabled = "true".equals(getAttribute(xssElt, ATT_DISABLED, "false"));
String enabled = xssElt.getAttribute(ATT_ENABLED);
if (StringUtils.hasText(enabled)) {
if (disabled) {
attrNotAllowed(parserContext, ATT_ENABLED, ATT_DISABLED, xssElt);
}
builder.addPropertyValue("enabled", enabled);
}
String block = xssElt.getAttribute(ATT_BLOCK);
if (StringUtils.hasText(block)) {
if (disabled) {
attrNotAllowed(parserContext, ATT_BLOCK, ATT_DISABLED, xssElt);
}
builder.addPropertyValue("block", block);
}
XXssProtectionHeaderWriter.HeaderValue headerValue = XXssProtectionHeaderWriter.HeaderValue
.from(xssElt.getAttribute(ATT_HEADER_VALUE));
if (headerValue != null) {

4
config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java

@ -1040,7 +1040,7 @@ public class ServerHttpSecurity { @@ -1040,7 +1040,7 @@ public class ServerHttpSecurity {
* X-Content-Type-Options: nosniff
* Strict-Transport-Security: max-age=31536000 ; includeSubDomains
* X-Frame-Options: DENY
* X-XSS-Protection: 1; mode=block
* X-XSS-Protection: 0
* </pre>
*
* such that "Strict-Transport-Security" is only added on secure requests.
@ -1081,7 +1081,7 @@ public class ServerHttpSecurity { @@ -1081,7 +1081,7 @@ public class ServerHttpSecurity {
* X-Content-Type-Options: nosniff
* Strict-Transport-Security: max-age=31536000 ; includeSubDomains
* X-Frame-Options: DENY
* X-XSS-Protection: 1; mode=block
* X-XSS-Protection: 0
* </pre>
*
* such that "Strict-Transport-Security" is only added on secure requests.

10
config/src/main/kotlin/org/springframework/security/config/annotation/web/headers/XssProtectionConfigDsl.kt

@ -25,18 +25,12 @@ import org.springframework.security.web.header.writers.XXssProtectionHeaderWrite @@ -25,18 +25,12 @@ import org.springframework.security.web.header.writers.XXssProtectionHeaderWrite
* idiomatic Kotlin code.
*
* @author Eleftheria Stein
* @author Daniel Garnier-Moiroux
* @since 5.3
* @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
@ -50,8 +44,6 @@ class XssProtectionConfigDsl { @@ -50,8 +44,6 @@ class XssProtectionConfigDsl {
internal fun get(): (HeadersConfigurer<HttpSecurity>.XXssConfig) -> Unit {
return { xssProtection ->
block?.also { xssProtection.block(block!!) }
xssProtectionEnabled?.also { xssProtection.xssProtectionEnabled(xssProtectionEnabled!!) }
headerValue?.also { xssProtection.headerValue(headerValue) }
if (disabled) {

8
config/src/main/resources/org/springframework/security/config/spring-security-6.0.rnc

@ -1268,13 +1268,7 @@ xss-protection.attlist &= @@ -1268,13 +1268,7 @@ xss-protection.attlist &=
## disable the X-XSS-Protection header. Default is 'false' meaning it is enabled.
attribute disabled {xsd:boolean}?
xss-protection.attlist &=
## specify that XSS Protection should be explicitly enabled or disabled. Default is 'true' meaning it is enabled.
attribute enabled {xsd:boolean}?
xss-protection.attlist &=
## Add mode=block to the header or not, default is on.
attribute block {xsd:boolean}?
xss-protection.attlist &=
## Specify the value for the X-Xss-Protection header. When set, overrides both enabled and block attributes.
## Specify the value for the X-Xss-Protection header. Defaults to "0".
attribute header-value {"0"|"1"|"1; mode=block"}?
content-type-options =

16
config/src/main/resources/org/springframework/security/config/spring-security-6.0.xsd

@ -3553,23 +3553,9 @@ @@ -3553,23 +3553,9 @@
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="enabled" type="xs:boolean">
<xs:annotation>
<xs:documentation>specify that XSS Protection should be explicitly enabled or disabled. Default is 'true'
meaning it is enabled.
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="block" type="xs:boolean">
<xs:annotation>
<xs:documentation>Add mode=block to the header or not, default is on.
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="header-value">
<xs:annotation>
<xs:documentation>Specify the value for the X-Xss-Protection header. When set, overrides both enabled and
block attributes.
<xs:documentation>Specify the value for the X-Xss-Protection header. Defaults to "0".
</xs:documentation>
</xs:annotation>
<xs:simpleType>

2
config/src/test/java/org/springframework/security/config/annotation/web/configuration/HttpSecurityConfigurationTests.java

@ -122,7 +122,7 @@ public class HttpSecurityConfigurationTests { @@ -122,7 +122,7 @@ public class HttpSecurityConfigurationTests {
.andExpect(header().string(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, max-age=0, must-revalidate"))
.andExpect(header().string(HttpHeaders.EXPIRES, "0"))
.andExpect(header().string(HttpHeaders.PRAGMA, "no-cache"))
.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "1; mode=block"))
.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "0"))
.andReturn();
// @formatter:on
assertThat(mvcResult.getResponse().getHeaderNames()).containsExactlyInAnyOrder(

2
config/src/test/java/org/springframework/security/config/annotation/web/configurers/HeadersConfigurerEagerHeadersTests.java

@ -57,7 +57,7 @@ public class HeadersConfigurerEagerHeadersTests { @@ -57,7 +57,7 @@ public class HeadersConfigurerEagerHeadersTests {
.andExpect(header().string(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, max-age=0, must-revalidate"))
.andExpect(header().string(HttpHeaders.EXPIRES, "0"))
.andExpect(header().string(HttpHeaders.PRAGMA, "no-cache"))
.andExpect(header().string("X-XSS-Protection", "1; mode=block"));
.andExpect(header().string("X-XSS-Protection", "0"));
}
@Configuration

28
config/src/test/java/org/springframework/security/config/annotation/web/configurers/HeadersConfigurerTests.java

@ -80,7 +80,7 @@ public class HeadersConfigurerTests { @@ -80,7 +80,7 @@ public class HeadersConfigurerTests {
.andExpect(header().string(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, max-age=0, must-revalidate"))
.andExpect(header().string(HttpHeaders.EXPIRES, "0"))
.andExpect(header().string(HttpHeaders.PRAGMA, "no-cache"))
.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "1; mode=block")).andReturn();
.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "0")).andReturn();
assertThat(mvcResult.getResponse().getHeaderNames()).containsExactlyInAnyOrder(
HttpHeaders.X_CONTENT_TYPE_OPTIONS, HttpHeaders.X_FRAME_OPTIONS, HttpHeaders.STRICT_TRANSPORT_SECURITY,
HttpHeaders.CACHE_CONTROL, HttpHeaders.EXPIRES, HttpHeaders.PRAGMA, HttpHeaders.X_XSS_PROTECTION);
@ -97,7 +97,7 @@ public class HeadersConfigurerTests { @@ -97,7 +97,7 @@ public class HeadersConfigurerTests {
.andExpect(header().string(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, max-age=0, must-revalidate"))
.andExpect(header().string(HttpHeaders.EXPIRES, "0"))
.andExpect(header().string(HttpHeaders.PRAGMA, "no-cache"))
.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "1; mode=block")).andReturn();
.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "0")).andReturn();
assertThat(mvcResult.getResponse().getHeaderNames()).containsExactlyInAnyOrder(
HttpHeaders.X_CONTENT_TYPE_OPTIONS, HttpHeaders.X_FRAME_OPTIONS, HttpHeaders.STRICT_TRANSPORT_SECURITY,
HttpHeaders.CACHE_CONTROL, HttpHeaders.EXPIRES, HttpHeaders.PRAGMA, HttpHeaders.X_XSS_PROTECTION);
@ -169,16 +169,16 @@ public class HeadersConfigurerTests { @@ -169,16 +169,16 @@ public class HeadersConfigurerTests {
throws Exception {
this.spring.register(XssProtectionConfig.class).autowire();
MvcResult mvcResult = this.mvc.perform(get("/").secure(true))
.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "1; mode=block")).andReturn();
.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "0")).andReturn();
assertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.X_XSS_PROTECTION);
}
@Test
public void getWhenHeaderDefaultsDisabledAndXssProtectionConfiguredValueDisabledThenOnlyXssProtectionHeaderInResponse()
public void getWhenHeaderDefaultsDisabledAndXssProtectionConfiguredEnabledModeBlockThenOnlyXssProtectionHeaderInResponse()
throws Exception {
this.spring.register(XssProtectionValueDisabledConfig.class).autowire();
this.spring.register(XssProtectionValueEnabledModeBlockConfig.class).autowire();
MvcResult mvcResult = this.mvc.perform(get("/").secure(true))
.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "0")).andReturn();
.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "1; mode=block")).andReturn();
assertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.X_XSS_PROTECTION);
}
@ -186,16 +186,16 @@ public class HeadersConfigurerTests { @@ -186,16 +186,16 @@ public class HeadersConfigurerTests {
public void getWhenOnlyXssProtectionConfiguredInLambdaThenOnlyXssProtectionHeaderInResponse() throws Exception {
this.spring.register(XssProtectionInLambdaConfig.class).autowire();
MvcResult mvcResult = this.mvc.perform(get("/").secure(true))
.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "1; mode=block")).andReturn();
.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "0")).andReturn();
assertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.X_XSS_PROTECTION);
}
@Test
public void getWhenHeaderDefaultsDisabledAndXssProtectionConfiguredValueDisabledInLambdaThenOnlyXssProtectionHeaderInResponse()
public void getWhenHeaderDefaultsDisabledAndXssProtectionConfiguredValueEnabledModeBlockInLambdaThenOnlyXssProtectionHeaderInResponse()
throws Exception {
this.spring.register(XssProtectionValueDisabledInLambdaConfig.class).autowire();
this.spring.register(XssProtectionValueEnabledModeBlockInLambdaConfig.class).autowire();
MvcResult mvcResult = this.mvc.perform(get("/").secure(true))
.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "0")).andReturn();
.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "1; mode=block")).andReturn();
assertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.X_XSS_PROTECTION);
}
@ -719,7 +719,7 @@ public class HeadersConfigurerTests { @@ -719,7 +719,7 @@ public class HeadersConfigurerTests {
@Configuration
@EnableWebSecurity
static class XssProtectionValueDisabledConfig {
static class XssProtectionValueEnabledModeBlockConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
@ -728,7 +728,7 @@ public class HeadersConfigurerTests { @@ -728,7 +728,7 @@ public class HeadersConfigurerTests {
.headers()
.defaultsDisabled()
.xssProtection()
.headerValue(XXssProtectionHeaderWriter.HeaderValue.DISABLED);
.headerValue(XXssProtectionHeaderWriter.HeaderValue.ENABLED_MODE_BLOCK);
// @formatter:on
return http.build();
}
@ -755,7 +755,7 @@ public class HeadersConfigurerTests { @@ -755,7 +755,7 @@ public class HeadersConfigurerTests {
@Configuration
@EnableWebSecurity
static class XssProtectionValueDisabledInLambdaConfig {
static class XssProtectionValueEnabledModeBlockInLambdaConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
@ -765,7 +765,7 @@ public class HeadersConfigurerTests { @@ -765,7 +765,7 @@ public class HeadersConfigurerTests {
headers
.defaultsDisabled()
.xssProtection((xXssConfig) ->
xXssConfig.headerValue(XXssProtectionHeaderWriter.HeaderValue.DISABLED)
xXssConfig.headerValue(XXssProtectionHeaderWriter.HeaderValue.ENABLED_MODE_BLOCK)
)
);
// @formatter:on

6
config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpHeadersTests.java

@ -62,7 +62,7 @@ public class NamespaceHttpHeadersTests { @@ -62,7 +62,7 @@ public class NamespaceHttpHeadersTests {
defaultHeaders.put("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate");
defaultHeaders.put("Expires", "0");
defaultHeaders.put("Pragma", "no-cache");
defaultHeaders.put("X-XSS-Protection", "1; mode=block");
defaultHeaders.put("X-XSS-Protection", "0");
}
public final SpringTestContext spring = new SpringTestContext(this);
@ -116,7 +116,7 @@ public class NamespaceHttpHeadersTests { @@ -116,7 +116,7 @@ public class NamespaceHttpHeadersTests {
@Test
public void requestWhenXssCustomThenBehaviorMatchesNamespace() throws Exception {
this.spring.register(XssProtectionCustomConfig.class).autowire();
this.mvc.perform(get("/")).andExpect(includes(Collections.singletonMap("X-XSS-Protection", "1")));
this.mvc.perform(get("/")).andExpect(includes(Collections.singletonMap("X-XSS-Protection", "1; mode=block")));
}
@Test
@ -291,7 +291,7 @@ public class NamespaceHttpHeadersTests { @@ -291,7 +291,7 @@ public class NamespaceHttpHeadersTests {
// xss-protection@enabled and xss-protection@block
.defaultsDisabled()
.xssProtection()
.headerValue(XXssProtectionHeaderWriter.HeaderValue.ENABLED);
.headerValue(XXssProtectionHeaderWriter.HeaderValue.ENABLED_MODE_BLOCK);
// @formatter:on
return http.build();
}

70
config/src/test/java/org/springframework/security/config/http/HttpHeadersConfigTests.java

@ -62,7 +62,7 @@ public class HttpHeadersConfigTests { @@ -62,7 +62,7 @@ public class HttpHeadersConfigTests {
.put("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate")
.put("Expires", "0")
.put("Pragma", "no-cache")
.put("X-XSS-Protection", "1; mode=block")
.put("X-XSS-Protection", "0")
.build();
// @formatter:on
@ -351,32 +351,6 @@ public class HttpHeadersConfigTests { @@ -351,32 +351,6 @@ public class HttpHeadersConfigTests {
excludedHeaders.remove("X-XSS-Protection");
this.spring.configLocations(this.xml("DefaultsDisabledWithXssProtection")).autowire();
// @formatter:off
this.mvc.perform(get("/"))
.andExpect(status().isOk())
.andExpect(header().string("X-XSS-Protection", "1; mode=block"))
.andExpect(excludes(excludedHeaders));
// @formatter:on
}
@Test
public void requestWhenEnablingXssProtectionThenDefaultsToModeBlock() throws Exception {
Set<String> excludedHeaders = new HashSet<>(defaultHeaders.keySet());
excludedHeaders.remove("X-XSS-Protection");
this.spring.configLocations(this.xml("DefaultsDisabledWithXssProtectionEnabled")).autowire();
// @formatter:off
this.mvc.perform(get("/"))
.andExpect(status().isOk())
.andExpect(header().string("X-XSS-Protection", "1; mode=block"))
.andExpect(excludes(excludedHeaders));
// @formatter:on
}
@Test
public void requestWhenDisablingXssProtectionThenDefaultsToZero() throws Exception {
Set<String> excludedHeaders = new HashSet<>(defaultHeaders.keySet());
excludedHeaders.remove("X-XSS-Protection");
this.spring.configLocations(this.xml("DefaultsDisabledWithXssProtectionDisabled")).autowire();
// @formatter:off
this.mvc.perform(get("/"))
.andExpect(status().isOk())
.andExpect(header().string("X-XSS-Protection", "0"))
@ -423,33 +397,6 @@ public class HttpHeadersConfigTests { @@ -423,33 +397,6 @@ public class HttpHeadersConfigTests {
// @formatter:on
}
@Test
public void requestWhenSettingXssProtectionDisabledHeaderValueToOneThenDefaultsToOne() throws Exception {
Set<String> excludedHeaders = new HashSet<>(defaultHeaders.keySet());
excludedHeaders.remove("X-XSS-Protection");
this.spring.configLocations(this.xml("DefaultsDisabledWithXssProtectionDisabledAndHeaderValueOne")).autowire();
// @formatter:off
this.mvc.perform(get("/"))
.andExpect(status().isOk())
.andExpect(header().string("X-XSS-Protection", "1"))
.andExpect(excludes(excludedHeaders));
// @formatter:on
}
@Test
public void configureWhenXssProtectionDisabledAndBlockSetThenAutowireFails() {
/*
* NOTE: Original error message "Cannot set block to true with enabled false" no
* longer shows up in stack trace as of Spring Framework 6.x.
*
* See https://github.com/spring-projects/spring-framework/issues/25162.
*/
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(() -> this.spring
.configLocations(this.xml("DefaultsDisabledWithXssProtectionDisabledAndBlockSet")).autowire())
.havingRootCause().withMessageContaining("Property 'block' threw exception");
}
@Test
public void requestWhenUsingCacheControlThenRespondsWithCorrespondingHeaders() throws Exception {
Map<String, String> includedHeaders = ImmutableMap.<String, String>builder()
@ -693,21 +640,6 @@ public class HttpHeadersConfigTests { @@ -693,21 +640,6 @@ public class HttpHeadersConfigTests {
.withMessageContaining("request-matcher-ref");
}
@Test
public void configureWhenXssProtectionDisabledAndEnabledThenAutowireFails() {
assertThatExceptionOfType(BeanDefinitionParsingException.class)
.isThrownBy(() -> this.spring.configLocations(this.xml("XssProtectionDisabledAndEnabled")).autowire())
.withMessageContaining("enabled");
}
@Test
public void configureWhenXssProtectionDisabledAndBlockSpecifiedThenAutowireFails() {
assertThatExceptionOfType(BeanDefinitionParsingException.class)
.isThrownBy(
() -> this.spring.configLocations(this.xml("XssProtectionDisabledSpecifyingBlock")).autowire())
.withMessageContaining("block");
}
@Test
public void configureWhenXssProtectionDisabledAndHeaderValueSpecifiedThenAutowireFails() {
assertThatExceptionOfType(BeanDefinitionParsingException.class).isThrownBy(

2
config/src/test/java/org/springframework/security/config/web/server/HeaderSpecTests.java

@ -75,7 +75,7 @@ public class HeaderSpecTests { @@ -75,7 +75,7 @@ public class HeaderSpecTests {
this.expectedHeaders.add(HttpHeaders.EXPIRES, "0");
this.expectedHeaders.add(ContentTypeOptionsServerHttpHeadersWriter.X_CONTENT_OPTIONS, "nosniff");
this.expectedHeaders.add(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS, "DENY");
this.expectedHeaders.add(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "1 ; mode=block");
this.expectedHeaders.add(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "0");
}
@Test

2
config/src/test/kotlin/org/springframework/security/config/annotation/web/HeadersDslTests.kt

@ -62,7 +62,7 @@ class HeadersDslTests { @@ -62,7 +62,7 @@ class HeadersDslTests {
header { string(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, max-age=0, must-revalidate") }
header { string(HttpHeaders.EXPIRES, "0") }
header { string(HttpHeaders.PRAGMA, "no-cache") }
header { string(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "1; mode=block") }
header { string(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "0") }
}
}

2
config/src/test/kotlin/org/springframework/security/config/annotation/web/HttpSecurityDslTests.kt

@ -106,7 +106,7 @@ class HttpSecurityDslTests { @@ -106,7 +106,7 @@ class HttpSecurityDslTests {
string(HttpHeaders.PRAGMA, "no-cache")
}
header {
string(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "1; mode=block")
string(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "0")
}
}
}

61
config/src/test/kotlin/org/springframework/security/config/annotation/web/headers/XssProtectionConfigDslTests.kt

@ -36,6 +36,7 @@ import org.springframework.test.web.servlet.get @@ -36,6 +36,7 @@ import org.springframework.test.web.servlet.get
* Tests for [XssProtectionConfigDsl]
*
* @author Eleftheria Stein
* @author Daniel Garnier-Moiroux
*/
@ExtendWith(SpringTestContextExtension::class)
class XssProtectionConfigDslTests {
@ -52,7 +53,7 @@ class XssProtectionConfigDslTests { @@ -52,7 +53,7 @@ class XssProtectionConfigDslTests {
this.mockMvc.get("/") {
secure = true
}.andExpect {
header { string(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "1; mode=block") }
header { string(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "0") }
}
}
@ -72,26 +73,25 @@ class XssProtectionConfigDslTests { @@ -72,26 +73,25 @@ class XssProtectionConfigDslTests {
}
@Test
fun `headers when XSS protection with block false then mode is not block in header`() {
this.spring.register(XssProtectionBlockFalseConfig::class.java).autowire()
fun `headers when XSS protection disabled then X-XSS-Protection header not in response`() {
this.spring.register(XssProtectionDisabledFunctionConfig::class.java).autowire()
this.mockMvc.get("/") {
secure = true
}.andExpect {
header { string(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "1") }
header { doesNotExist(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION) }
}
}
@Configuration
@EnableWebSecurity
open class XssProtectionBlockFalseConfig {
open class XssProtectionDisabledFunctionConfig {
@Bean
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
headers {
defaultsDisabled = true
xssProtection {
block = false
disable()
}
}
}
@ -100,53 +100,26 @@ class XssProtectionConfigDslTests { @@ -100,53 +100,26 @@ class XssProtectionConfigDslTests {
}
@Test
fun `headers when XSS protection disabled then X-XSS-Protection header is 0`() {
this.spring.register(XssProtectionDisabledConfig::class.java).autowire()
fun `headers when XSS protection header value enabled then X-XSS-Protection header is 1`() {
this.spring.register(XssProtectionHeaderValueEnabledFunctionConfig::class.java).autowire()
this.mockMvc.get("/") {
secure = true
}.andExpect {
header { string(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "0") }
header { string(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "1") }
}
}
@Configuration
@EnableWebSecurity
open class XssProtectionDisabledConfig {
open class XssProtectionHeaderValueEnabledFunctionConfig {
@Bean
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
headers {
defaultsDisabled = true
xssProtection {
xssProtectionEnabled = false
}
}
}
return http.build()
}
}
@Test
fun `headers when XSS protection disabled then X-XSS-Protection header not in response`() {
this.spring.register(XssProtectionDisabledFunctionConfig::class.java).autowire()
this.mockMvc.get("/") {
secure = true
}.andExpect {
header { doesNotExist(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION) }
}
}
@Configuration
@EnableWebSecurity
open class XssProtectionDisabledFunctionConfig {
@Bean
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
headers {
xssProtection {
disable()
headerValue = XXssProtectionHeaderWriter.HeaderValue.ENABLED
}
}
}
@ -155,26 +128,26 @@ class XssProtectionConfigDslTests { @@ -155,26 +128,26 @@ 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()
fun `headers when XSS protection header value enabled_mode_block then X-XSS-Protection header is 1 and mode=block`() {
this.spring.register(XssProtectionHeaderValueEnabledModeBlockFunctionConfig::class.java).autowire()
this.mockMvc.get("/") {
secure = true
}.andExpect {
header { string(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "0") }
header { string(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "1; mode=block") }
}
}
@Configuration
@EnableWebSecurity
open class XssProtectionHeaderValueDisabledFunctionConfig () {
open class XssProtectionHeaderValueEnabledModeBlockFunctionConfig {
@Bean
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
headers {
defaultsDisabled = true
xssProtection {
headerValue = XXssProtectionHeaderWriter.HeaderValue.DISABLED
headerValue = XXssProtectionHeaderWriter.HeaderValue.ENABLED_MODE_BLOCK
}
}
}

2
config/src/test/kotlin/org/springframework/security/config/web/server/ServerHeadersDslTests.kt

@ -71,7 +71,7 @@ class ServerHeadersDslTests { @@ -71,7 +71,7 @@ class ServerHeadersDslTests {
.expectHeader().valueEquals(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, max-age=0, must-revalidate")
.expectHeader().valueEquals(HttpHeaders.EXPIRES, "0")
.expectHeader().valueEquals(HttpHeaders.PRAGMA, "no-cache")
.expectHeader().valueEquals(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "1 ; mode=block")
.expectHeader().valueEquals(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "0")
}
@Configuration

2
config/src/test/kotlin/org/springframework/security/config/web/server/ServerHttpSecurityDslTests.kt

@ -126,7 +126,7 @@ class ServerHttpSecurityDslTests { @@ -126,7 +126,7 @@ class ServerHttpSecurityDslTests {
.expectHeader().valueEquals(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, max-age=0, must-revalidate")
.expectHeader().valueEquals(HttpHeaders.EXPIRES, "0")
.expectHeader().valueEquals(HttpHeaders.PRAGMA, "no-cache")
.expectHeader().valueEquals(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "1 ; mode=block")
.expectHeader().valueEquals(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "0")
}
@Configuration

6
config/src/test/kotlin/org/springframework/security/config/web/server/ServerXssProtectionDslTests.kt

@ -57,7 +57,7 @@ class ServerXssProtectionDslTests { @@ -57,7 +57,7 @@ class ServerXssProtectionDslTests {
this.client.get()
.uri("/")
.exchange()
.expectHeader().valueEquals(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "1 ; mode=block")
.expectHeader().valueEquals(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "0")
}
@Configuration
@ -107,7 +107,7 @@ class ServerXssProtectionDslTests { @@ -107,7 +107,7 @@ class ServerXssProtectionDslTests {
this.client.get()
.uri("/")
.exchange()
.expectHeader().valueEquals(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "0")
.expectHeader().valueEquals(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "1")
}
@EnableWebFluxSecurity
@ -118,7 +118,7 @@ class ServerXssProtectionDslTests { @@ -118,7 +118,7 @@ class ServerXssProtectionDslTests {
return http {
headers {
xssProtection {
headerValue = XXssProtectionServerHttpHeadersWriter.HeaderValue.DISABLED
headerValue = XXssProtectionServerHttpHeadersWriter.HeaderValue.ENABLED
}
}
}

37
config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithXssProtectionDisabled.xml

@ -1,37 +0,0 @@ @@ -1,37 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2002-2018 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.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/security"
xsi:schemaLocation="
http://www.springframework.org/schema/security
https://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<http auto-config="true">
<headers defaults-disabled="true">
<xss-protection enabled="false"/>
</headers>
<intercept-url pattern="/**" access="permitAll"/>
</http>
<b:bean name="simple" class="org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController"/>
<b:import resource="userservice.xml"/>
</b:beans>

37
config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithXssProtectionDisabledAndBlockSet.xml

@ -1,37 +0,0 @@ @@ -1,37 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2002-2018 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.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/security"
xsi:schemaLocation="
http://www.springframework.org/schema/security
https://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<http auto-config="true">
<headers defaults-disabled="true">
<xss-protection enabled="false" block="true"/>
</headers>
<intercept-url pattern="/**" access="permitAll"/>
</http>
<b:bean name="simple" class="org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController"/>
<b:import resource="userservice.xml"/>
</b:beans>

37
config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithXssProtectionDisabledAndHeaderValueOne.xml

@ -1,37 +0,0 @@ @@ -1,37 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/security"
xsi:schemaLocation="
http://www.springframework.org/schema/security
https://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<http auto-config="true">
<headers defaults-disabled="true">
<xss-protection enabled="false" header-value="1"/>
</headers>
<intercept-url pattern="/**" access="permitAll"/>
</http>
<b:bean name="simple" class="org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController"/>
<b:import resource="userservice.xml"/>
</b:beans>

37
config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithXssProtectionEnabled.xml

@ -1,37 +0,0 @@ @@ -1,37 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2002-2018 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.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/security"
xsi:schemaLocation="
http://www.springframework.org/schema/security
https://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<http auto-config="true">
<headers defaults-disabled="true">
<xss-protection enabled="true"/>
</headers>
<intercept-url pattern="/**" access="permitAll"/>
</http>
<b:bean name="simple" class="org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController"/>
<b:import resource="userservice.xml"/>
</b:beans>

37
config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-XssProtectionDisabledAndEnabled.xml

@ -1,37 +0,0 @@ @@ -1,37 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2002-2018 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.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/security"
xsi:schemaLocation="
http://www.springframework.org/schema/security
https://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<http auto-config="true">
<headers>
<xss-protection disabled="true" enabled="true"/>
</headers>
<intercept-url pattern="/**" access="permitAll"/>
</http>
<b:bean name="simple" class="org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController"/>
<b:import resource="userservice.xml"/>
</b:beans>

37
config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-XssProtectionDisabledSpecifyingBlock.xml

@ -1,37 +0,0 @@ @@ -1,37 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2002-2018 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.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/security"
xsi:schemaLocation="
http://www.springframework.org/schema/security
https://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<http auto-config="true">
<headers>
<xss-protection disabled="true" block="true"/>
</headers>
<intercept-url pattern="/**" access="permitAll"/>
</http>
<b:bean name="simple" class="org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController"/>
<b:import resource="userservice.xml"/>
</b:beans>

10
docs/modules/ROOT/pages/features/exploits/headers.adoc

@ -33,7 +33,7 @@ Expires: 0 @@ -33,7 +33,7 @@ Expires: 0
X-Content-Type-Options: nosniff
Strict-Transport-Security: max-age=31536000 ; includeSubDomains
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
X-XSS-Protection: 0
----
====
@ -209,18 +209,14 @@ See the relevant sections to see how to customize the defaults for both xref:ser @@ -209,18 +209,14 @@ See the relevant sections to see how to customize the defaults for both xref:ser
====
Some browsers have built-in support for filtering out https://www.owasp.org/index.php/Testing_for_Reflected_Cross_site_scripting_(OWASP-DV-001)[reflected XSS attacks].
This is by no means foolproof but does assist in XSS protection.
The filter has been deprecated in major browsers, and https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#x-xss-protection[current OWASP recommendation] is to explicitly set the header to 0.
The filtering is typically enabled by default, so adding the header typically just ensures it is enabled and instructs the browser what to do when a XSS attack is detected.
For example, the filter might try to change the content in the least invasive way to still render everything.
At times, this type of replacement can become an https://hackademix.net/2009/11/21/ies-xss-filter-creates-xss-vulnerabilities/[XSS vulnerability in itself].
Instead, it is best to block the content rather than attempt to fix it.
By default, Spring Security blocks the content by using the following header:
====
[source]
----
X-XSS-Protection: 1; mode=block
X-XSS-Protection: 0
----
====

39
docs/modules/ROOT/pages/reactive/exploits/headers.adoc

@ -255,8 +255,8 @@ fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { @@ -255,8 +255,8 @@ fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
[[webflux-headers-xss-protection]]
== X-XSS-Protection
By default, Spring Security instructs browsers to block reflected XSS attacks by using the <<headers-xss-protection,X-XSS-Protection header>.
You can disable `X-XSS-Protection`:
By default, Spring Security instructs browsers to disable the XSS Auditor by using <<headers-xss-protection,X-XSS-Protection header>.
You can disable the `X-XSS-Protection` header entirely:
.X-XSS-Protection Customization
====
@ -291,6 +291,41 @@ fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { @@ -291,6 +291,41 @@ fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
----
====
You can also change the header value:
.X-XSS-Protection Explicit header value
====
.Java
[source,java,role="primary"]
----
@Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http
// ...
.headers(headers -> headers
.xssProtection(xssProtection -> xssProtection.headerValue(XXssProtectionServerHttpHeadersWriter.HeaderValue.ENABLED_MODE_BLOCK))
);
return http.build();
}
----
.Kotlin
[source,kotlin,role="secondary"]
----
@Bean
fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
// ...
headers {
xssProtection {
headerValue = XXssProtectionServerHttpHeadersWriter.HeaderValue.ENABLED_MODE_BLOCK
}
}
}
}
----
====
[[webflux-headers-csp]]
== Content Security Policy (CSP)
By default, Spring Security does not add xref:features/exploits/headers.adoc#headers-csp[Content Security Policy], because a reasonable default is impossible to know without the context of the application.

15
docs/modules/ROOT/pages/servlet/appendix/namespace/http.adoc

@ -571,23 +571,10 @@ This is in no-way a full protection to XSS attacks! @@ -571,23 +571,10 @@ This is in no-way a full protection to XSS attacks!
Do not include the header for https://en.wikipedia.org/wiki/Cross-site_scripting#Non-Persistent[reflected / Type-1 Cross-Site Scripting (XSS)] protection.
[[nsa-xss-protection-enabled]]
* **xss-protection-enabled**
Explicitly enable or disable https://en.wikipedia.org/wiki/Cross-site_scripting#Non-Persistent[reflected / Type-1 Cross-Site Scripting (XSS)] protection.
[[nsa-xss-protection-block]]
* **xss-protection-block**
When true and xss-protection-enabled is true, adds mode=block to the header.
This indicates to the browser that the page should not be loaded at all.
When false and xss-protection-enabled is true, the page will still be rendered when an reflected attack is detected but the response will be modified to protect against the attack.
Note that there are sometimes ways of bypassing this mode which can often times make blocking the page more desirable.
[[nsa-xss-protection-header-value]]
* **xss-protection-header-value**
Explicitly set the value for https://en.wikipedia.org/wiki/Cross-site_scripting#Non-Persistent[reflected / Type-1 Cross-Site Scripting (XSS)] header.
One of: "0", "1", "1; mode=block".
When set, overrides both enabled and block attributes.
One of: "0", "1", "1; mode=block". Defaults to "0".
[[nsa-xss-protection-parents]]

11
docs/modules/ROOT/pages/servlet/exploits/headers.adoc

@ -529,9 +529,10 @@ class SecurityConfig { @@ -529,9 +529,10 @@ class SecurityConfig {
[[servlet-headers-xss-protection]]
== X-XSS-Protection
By default, Spring Security instructs browsers to block reflected XSS attacks by using the <<headers-xss-protection,X-XSS-Protection header>.
By default, Spring Security instructs browsers to disable the XSS Auditor by using <<headers-xss-protection,X-XSS-Protection header>.
However, you can change this default.
For example, the following configuration specifies that Spring Security should no longer instruct browsers to block the content:
For example, the following configuration specifies that Spring Security instruct compatible browsers to enable filtering,
and block the content:
.X-XSS-Protection Customization
====
@ -548,7 +549,7 @@ public class WebSecurityConfig { @@ -548,7 +549,7 @@ public class WebSecurityConfig {
// ...
.headers(headers -> headers
.xssProtection(xss -> xss
.block(false)
.headerValue(XXssProtectionHeaderWriter.HeaderValue.ENABLED_MODE_BLOCK)
)
);
return http.build();
@ -563,7 +564,7 @@ public class WebSecurityConfig { @@ -563,7 +564,7 @@ public class WebSecurityConfig {
<!-- ... -->
<headers>
<xss-protection block="false"/>
<xss-protection headerValue="1; mode=block"/>
</headers>
</http>
----
@ -581,7 +582,7 @@ class SecurityConfig { @@ -581,7 +582,7 @@ class SecurityConfig {
http {
headers {
xssProtection {
block = false
headerValue = XXssProtectionHeaderWriter.HeaderValue.ENABLED_MODE_BLOCK
}
}
}

52
web/src/main/java/org/springframework/security/web/header/writers/XXssProtectionHeaderWriter.java

@ -29,6 +29,7 @@ import org.springframework.util.Assert; @@ -29,6 +29,7 @@ import org.springframework.util.Assert;
*
* @author Rob Winch
* @author Ankur Pathak
* @author Daniel Garnier-Moiroux
* @since 3.2
*/
public final class XXssProtectionHeaderWriter implements HeaderWriter {
@ -41,7 +42,7 @@ public final class XXssProtectionHeaderWriter implements HeaderWriter { @@ -41,7 +42,7 @@ public final class XXssProtectionHeaderWriter implements HeaderWriter {
* Create a new instance
*/
public XXssProtectionHeaderWriter() {
this.headerValue = HeaderValue.ENABLED_MODE_BLOCK;
this.headerValue = HeaderValue.DISABLED;
}
@Override
@ -51,55 +52,6 @@ public final class XXssProtectionHeaderWriter implements HeaderWriter { @@ -51,55 +52,6 @@ public final class XXssProtectionHeaderWriter implements HeaderWriter {
}
}
/**
* If true, will contain a value of 1. For example:
*
* <pre>
* X-XSS-Protection: 1
* </pre>
*
* or if {@link #setBlock(boolean)} is true
*
*
* <pre>
* X-XSS-Protection: 1; mode=block
* </pre>
*
* If false, will explicitly disable specify that X-XSS-Protection is disabled. For
* example:
*
* <pre>
* 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) {
this.headerValue = HeaderValue.DISABLED;
}
else if (this.headerValue == HeaderValue.DISABLED) {
this.headerValue = HeaderValue.ENABLED;
}
}
/**
* 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.headerValue == HeaderValue.DISABLED && block) {
throw new IllegalArgumentException("Cannot set block to true with enabled false");
}
this.headerValue = block ? HeaderValue.ENABLED_MODE_BLOCK : HeaderValue.ENABLED;
}
/**
* Sets the value of the X-XSS-PROTECTION header.
* <p>

55
web/src/main/java/org/springframework/security/web/server/header/XXssProtectionServerHttpHeadersWriter.java

@ -41,7 +41,7 @@ public class XXssProtectionServerHttpHeadersWriter implements ServerHttpHeadersW @@ -41,7 +41,7 @@ public class XXssProtectionServerHttpHeadersWriter implements ServerHttpHeadersW
* Creates a new instance
*/
public XXssProtectionServerHttpHeadersWriter() {
this.headerValue = HeaderValue.ENABLED_MODE_BLOCK;
this.headerValue = HeaderValue.DISABLED;
updateDelegate();
}
@ -51,57 +51,8 @@ public class XXssProtectionServerHttpHeadersWriter implements ServerHttpHeadersW @@ -51,57 +51,8 @@ public class XXssProtectionServerHttpHeadersWriter implements ServerHttpHeadersW
}
/**
* If true, will contain a value of 1. For example:
*
* <pre>
* X-XSS-Protection: 1
* </pre>
*
* or if {@link #setBlock(boolean)} is true
*
*
* <pre>
* X-XSS-Protection: 1; mode=block
* </pre>
*
* If false, will explicitly disable specify that X-XSS-Protection is disabled. For
* example:
*
* <pre>
* 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) {
this.headerValue = HeaderValue.DISABLED;
}
else if (this.headerValue == HeaderValue.DISABLED) {
this.headerValue = HeaderValue.ENABLED;
}
updateDelegate();
}
/**
* 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.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.
* Sets the value of the X-XSS-PROTECTION header. Defaults to
* {@link HeaderValue#DISABLED}
* <p>
* If {@link HeaderValue#DISABLED}, will specify that X-XSS-Protection is disabled.
* For example:

33
web/src/test/java/org/springframework/security/web/header/writers/XXssProtectionHeaderWriterTests.java

@ -28,6 +28,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException @@ -28,6 +28,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
/**
* @author Rob Winch
* @author Ankur Pathak
* @author Daniel Garnier-Moiroux
*
*/
public class XXssProtectionHeaderWriterTests {
@ -49,43 +50,11 @@ public class XXssProtectionHeaderWriterTests { @@ -49,43 +50,11 @@ public class XXssProtectionHeaderWriterTests {
@Test
public void writeHeaders() {
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 writeHeadersNoBlock() {
this.writer.setBlock(false);
this.writer.writeHeaders(this.request, this.response);
assertThat(this.response.getHeaderNames()).hasSize(1);
assertThat(this.response.getHeaderValues("X-XSS-Protection")).containsOnly("1");
}
@Test
public void writeHeadersDisabled() {
this.writer.setBlock(false);
this.writer.setEnabled(false);
this.writer.writeHeaders(this.request, this.response);
assertThat(this.response.getHeaderNames()).hasSize(1);
assertThat(this.response.getHeaderValues("X-XSS-Protection")).containsOnly("0");
}
@Test
public void setEnabledFalseWithBlockTrue() {
this.writer.setEnabled(false);
this.writer.writeHeaders(this.request, this.response);
assertThat(this.response.getHeaderNames()).hasSize(1);
assertThat(this.response.getHeaderValues("X-XSS-Protection")).containsOnly("0");
}
@Test
public void setBlockTrueWithEnabledFalse() {
this.writer.setBlock(false);
this.writer.setEnabled(false);
assertThatIllegalArgumentException().isThrownBy(() -> this.writer.setBlock(true));
}
@Test
public void writeHeaderWhenNotPresent() {
String value = new String("value");

17
web/src/test/java/org/springframework/security/web/server/header/XXssProtectionServerHttpHeadersWriterTests.java

@ -46,23 +46,6 @@ public class XXssProtectionServerHttpHeadersWriterTests { @@ -46,23 +46,6 @@ public class XXssProtectionServerHttpHeadersWriterTests {
@Test
public void writeHeadersWhenNoHeadersThenWriteHeaders() {
this.writer.writeHttpHeaders(this.exchange);
assertThat(this.headers).hasSize(1);
assertThat(this.headers.get(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION))
.containsOnly("1 ; mode=block");
}
@Test
public void writeHeadersWhenBlockFalseThenWriteHeaders() {
this.writer.setBlock(false);
this.writer.writeHttpHeaders(this.exchange);
assertThat(this.headers).hasSize(1);
assertThat(this.headers.get(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION)).containsOnly("1");
}
@Test
public void writeHeadersWhenEnabledFalseThenWriteHeaders() {
this.writer.setEnabled(false);
this.writer.writeHttpHeaders(this.exchange);
assertThat(this.headers).hasSize(1);
assertThat(this.headers.get(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION)).containsOnly("0");

Loading…
Cancel
Save