Skip to content

Commit

Permalink
Default X-Xss-Protection header value to "0"
Browse files Browse the repository at this point in the history
Closes gh-9631
  • Loading branch information
Kehrlann authored and sjohnr committed Oct 7, 2022
1 parent dcda899 commit 27059ce
Show file tree
Hide file tree
Showing 32 changed files with 105 additions and 637 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
* 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
Expand All @@ -73,6 +73,7 @@
* @author Eddú Meléndez
* @author Vedran Pavic
* @author Ankur Pathak
* @author Daniel Garnier-Moiroux
* @since 3.2
*/
public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
Expand Down Expand Up @@ -733,50 +734,6 @@ private XXssConfig() {
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}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -583,20 +579,6 @@ private void parseXssElement(boolean addIfNotPresent, Element element, ParserCon
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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1040,7 +1040,7 @@ public ServerHttpSecurity oauth2ResourceServer(
* 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.
Expand Down Expand Up @@ -1081,7 +1081,7 @@ public HeaderSpec headers() {
* 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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ public void getWhenDefaultFilterChainBeanThenDefaultHeadersInResponse() throws E
.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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public void requestWhenHeadersEagerlyConfiguredThenHeadersAreWritten() throws Ex
.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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ public void getWhenHeadersConfiguredThenDefaultHeadersInResponse() throws Except
.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);
Expand All @@ -97,7 +97,7 @@ public void getWhenHeadersConfiguredInLambdaThenDefaultHeadersInResponse() throw
.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);
Expand Down Expand Up @@ -169,33 +169,33 @@ public void getWhenHeaderDefaultsDisabledAndXssProtectionConfiguredThenOnlyXssPr
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);
}

@Test
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);
}

Expand Down Expand Up @@ -719,7 +719,7 @@ SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

@Configuration
@EnableWebSecurity
static class XssProtectionValueDisabledConfig {
static class XssProtectionValueEnabledModeBlockConfig {

@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
Expand All @@ -728,7 +728,7 @@ SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
.headers()
.defaultsDisabled()
.xssProtection()
.headerValue(XXssProtectionHeaderWriter.HeaderValue.DISABLED);
.headerValue(XXssProtectionHeaderWriter.HeaderValue.ENABLED_MODE_BLOCK);
// @formatter:on
return http.build();
}
Expand All @@ -755,7 +755,7 @@ SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

@Configuration
@EnableWebSecurity
static class XssProtectionValueDisabledInLambdaConfig {
static class XssProtectionValueEnabledModeBlockInLambdaConfig {

@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
Expand All @@ -765,7 +765,7 @@ SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
headers
.defaultsDisabled()
.xssProtection((xXssConfig) ->
xXssConfig.headerValue(XXssProtectionHeaderWriter.HeaderValue.DISABLED)
xXssConfig.headerValue(XXssProtectionHeaderWriter.HeaderValue.ENABLED_MODE_BLOCK)
)
);
// @formatter:on
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -116,7 +116,7 @@ public void requestWhenXssOnlyThenBehaviorMatchesNamespace() throws Exception {
@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
Expand Down Expand Up @@ -291,7 +291,7 @@ SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// xss-protection@enabled and xss-protection@block
.defaultsDisabled()
.xssProtection()
.headerValue(XXssProtectionHeaderWriter.HeaderValue.ENABLED);
.headerValue(XXssProtectionHeaderWriter.HeaderValue.ENABLED_MODE_BLOCK);
// @formatter:on
return http.build();
}
Expand Down
Loading

0 comments on commit 27059ce

Please sign in to comment.