Skip to content

Commit 27059ce

Browse files
KehrlannSteve Riesenberg
authored and
Steve Riesenberg
committed
Default X-Xss-Protection header value to "0"
Closes gh-9631
1 parent dcda899 commit 27059ce

File tree

32 files changed

+105
-637
lines changed

32 files changed

+105
-637
lines changed

Diff for: config/src/main/java/org/springframework/security/config/annotation/web/configurers/HeadersConfigurer.java

+2-45
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464
* X-Content-Type-Options: nosniff
6565
* Strict-Transport-Security: max-age=31536000 ; includeSubDomains
6666
* X-Frame-Options: DENY
67-
* X-XSS-Protection: 1; mode=block
67+
* X-XSS-Protection: 0
6868
* </pre>
6969
*
7070
* @author Rob Winch
@@ -73,6 +73,7 @@
7373
* @author Eddú Meléndez
7474
* @author Vedran Pavic
7575
* @author Ankur Pathak
76+
* @author Daniel Garnier-Moiroux
7677
* @since 3.2
7778
*/
7879
public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
@@ -733,50 +734,6 @@ private XXssConfig() {
733734
enable();
734735
}
735736

736-
/**
737-
* If false, will not specify the mode as blocked. In this instance, any content
738-
* will be attempted to be fixed. If true, the content will be replaced with "#".
739-
* @param enabled the new value
740-
* @deprecated use
741-
* {@link XXssConfig#headerValue(XXssProtectionHeaderWriter.HeaderValue)} instead
742-
*/
743-
@Deprecated
744-
public XXssConfig block(boolean enabled) {
745-
this.writer.setBlock(enabled);
746-
return this;
747-
}
748-
749-
/**
750-
* If true, the header value will contain a value of 1. For example:
751-
*
752-
* <pre>
753-
* X-XSS-Protection: 1
754-
* </pre>
755-
*
756-
* or if {@link XXssProtectionHeaderWriter#setBlock(boolean)} of the given
757-
* {@link XXssProtectionHeaderWriter} is true
758-
*
759-
*
760-
* <pre>
761-
* X-XSS-Protection: 1; mode=block
762-
* </pre>
763-
*
764-
* If false, will explicitly disable specify that X-XSS-Protection is disabled.
765-
* For example:
766-
*
767-
* <pre>
768-
* X-XSS-Protection: 0
769-
* </pre>
770-
* @param enabled the new value
771-
* @deprecated use
772-
* {@link XXssConfig#headerValue(XXssProtectionHeaderWriter.HeaderValue)} instead
773-
*/
774-
@Deprecated
775-
public XXssConfig xssProtectionEnabled(boolean enabled) {
776-
this.writer.setEnabled(enabled);
777-
return this;
778-
}
779-
780737
/**
781738
* Sets the value of the X-XSS-PROTECTION header. OWASP recommends using
782739
* {@link XXssProtectionHeaderWriter.HeaderValue#DISABLED}.

Diff for: config/src/main/java/org/springframework/security/config/http/HeadersBeanDefinitionParser.java

-18
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,6 @@ public class HeadersBeanDefinitionParser implements BeanDefinitionParser {
6969

7070
private static final String ATT_DISABLED = "disabled";
7171

72-
private static final String ATT_ENABLED = "enabled";
73-
74-
private static final String ATT_BLOCK = "block";
75-
7672
private static final String ATT_POLICY = "policy";
7773

7874
private static final String ATT_STRATEGY = "strategy";
@@ -583,20 +579,6 @@ private void parseXssElement(boolean addIfNotPresent, Element element, ParserCon
583579
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(XXssProtectionHeaderWriter.class);
584580
if (xssElt != null) {
585581
boolean disabled = "true".equals(getAttribute(xssElt, ATT_DISABLED, "false"));
586-
String enabled = xssElt.getAttribute(ATT_ENABLED);
587-
if (StringUtils.hasText(enabled)) {
588-
if (disabled) {
589-
attrNotAllowed(parserContext, ATT_ENABLED, ATT_DISABLED, xssElt);
590-
}
591-
builder.addPropertyValue("enabled", enabled);
592-
}
593-
String block = xssElt.getAttribute(ATT_BLOCK);
594-
if (StringUtils.hasText(block)) {
595-
if (disabled) {
596-
attrNotAllowed(parserContext, ATT_BLOCK, ATT_DISABLED, xssElt);
597-
}
598-
builder.addPropertyValue("block", block);
599-
}
600582
XXssProtectionHeaderWriter.HeaderValue headerValue = XXssProtectionHeaderWriter.HeaderValue
601583
.from(xssElt.getAttribute(ATT_HEADER_VALUE));
602584
if (headerValue != null) {

Diff for: config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -1040,7 +1040,7 @@ public ServerHttpSecurity oauth2ResourceServer(
10401040
* X-Content-Type-Options: nosniff
10411041
* Strict-Transport-Security: max-age=31536000 ; includeSubDomains
10421042
* X-Frame-Options: DENY
1043-
* X-XSS-Protection: 1; mode=block
1043+
* X-XSS-Protection: 0
10441044
* </pre>
10451045
*
10461046
* such that "Strict-Transport-Security" is only added on secure requests.
@@ -1081,7 +1081,7 @@ public HeaderSpec headers() {
10811081
* X-Content-Type-Options: nosniff
10821082
* Strict-Transport-Security: max-age=31536000 ; includeSubDomains
10831083
* X-Frame-Options: DENY
1084-
* X-XSS-Protection: 1; mode=block
1084+
* X-XSS-Protection: 0
10851085
* </pre>
10861086
*
10871087
* such that "Strict-Transport-Security" is only added on secure requests.

Diff for: config/src/main/kotlin/org/springframework/security/config/annotation/web/headers/XssProtectionConfigDsl.kt

+1-9
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,12 @@ import org.springframework.security.web.header.writers.XXssProtectionHeaderWrite
2525
* idiomatic Kotlin code.
2626
*
2727
* @author Eleftheria Stein
28+
* @author Daniel Garnier-Moiroux
2829
* @since 5.3
29-
* @property block whether to specify the mode as blocked
30-
* @property xssProtectionEnabled if true, the header value will contain a value of 1.
31-
* If false, will explicitly disable specify that X-XSS-Protection is disabled.
3230
* @property headerValue the value of the X-XSS-Protection header. OWASP recommends [HeaderValue.DISABLED].
3331
*/
3432
@HeadersSecurityMarker
3533
class XssProtectionConfigDsl {
36-
@Deprecated("use headerValue instead")
37-
var block: Boolean? = null
38-
@Deprecated("use headerValue instead")
39-
var xssProtectionEnabled: Boolean? = null
4034
var headerValue: HeaderValue? = null
4135

4236
private var disabled = false
@@ -50,8 +44,6 @@ class XssProtectionConfigDsl {
5044

5145
internal fun get(): (HeadersConfigurer<HttpSecurity>.XXssConfig) -> Unit {
5246
return { xssProtection ->
53-
block?.also { xssProtection.block(block!!) }
54-
xssProtectionEnabled?.also { xssProtection.xssProtectionEnabled(xssProtectionEnabled!!) }
5547
headerValue?.also { xssProtection.headerValue(headerValue) }
5648

5749
if (disabled) {

Diff for: config/src/main/resources/org/springframework/security/config/spring-security-6.0.rnc

+1-7
Original file line numberDiff line numberDiff line change
@@ -1268,13 +1268,7 @@ xss-protection.attlist &=
12681268
## disable the X-XSS-Protection header. Default is 'false' meaning it is enabled.
12691269
attribute disabled {xsd:boolean}?
12701270
xss-protection.attlist &=
1271-
## specify that XSS Protection should be explicitly enabled or disabled. Default is 'true' meaning it is enabled.
1272-
attribute enabled {xsd:boolean}?
1273-
xss-protection.attlist &=
1274-
## Add mode=block to the header or not, default is on.
1275-
attribute block {xsd:boolean}?
1276-
xss-protection.attlist &=
1277-
## Specify the value for the X-Xss-Protection header. When set, overrides both enabled and block attributes.
1271+
## Specify the value for the X-Xss-Protection header. Defaults to "0".
12781272
attribute header-value {"0"|"1"|"1; mode=block"}?
12791273

12801274
content-type-options =

Diff for: config/src/main/resources/org/springframework/security/config/spring-security-6.0.xsd

+1-15
Original file line numberDiff line numberDiff line change
@@ -3553,23 +3553,9 @@
35533553
</xs:documentation>
35543554
</xs:annotation>
35553555
</xs:attribute>
3556-
<xs:attribute name="enabled" type="xs:boolean">
3557-
<xs:annotation>
3558-
<xs:documentation>specify that XSS Protection should be explicitly enabled or disabled. Default is 'true'
3559-
meaning it is enabled.
3560-
</xs:documentation>
3561-
</xs:annotation>
3562-
</xs:attribute>
3563-
<xs:attribute name="block" type="xs:boolean">
3564-
<xs:annotation>
3565-
<xs:documentation>Add mode=block to the header or not, default is on.
3566-
</xs:documentation>
3567-
</xs:annotation>
3568-
</xs:attribute>
35693556
<xs:attribute name="header-value">
35703557
<xs:annotation>
3571-
<xs:documentation>Specify the value for the X-Xss-Protection header. When set, overrides both enabled and
3572-
block attributes.
3558+
<xs:documentation>Specify the value for the X-Xss-Protection header. Defaults to "0".
35733559
</xs:documentation>
35743560
</xs:annotation>
35753561
<xs:simpleType>

Diff for: config/src/test/java/org/springframework/security/config/annotation/web/configuration/HttpSecurityConfigurationTests.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ public void getWhenDefaultFilterChainBeanThenDefaultHeadersInResponse() throws E
122122
.andExpect(header().string(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, max-age=0, must-revalidate"))
123123
.andExpect(header().string(HttpHeaders.EXPIRES, "0"))
124124
.andExpect(header().string(HttpHeaders.PRAGMA, "no-cache"))
125-
.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "1; mode=block"))
125+
.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "0"))
126126
.andReturn();
127127
// @formatter:on
128128
assertThat(mvcResult.getResponse().getHeaderNames()).containsExactlyInAnyOrder(

Diff for: config/src/test/java/org/springframework/security/config/annotation/web/configurers/HeadersConfigurerEagerHeadersTests.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public void requestWhenHeadersEagerlyConfiguredThenHeadersAreWritten() throws Ex
5757
.andExpect(header().string(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, max-age=0, must-revalidate"))
5858
.andExpect(header().string(HttpHeaders.EXPIRES, "0"))
5959
.andExpect(header().string(HttpHeaders.PRAGMA, "no-cache"))
60-
.andExpect(header().string("X-XSS-Protection", "1; mode=block"));
60+
.andExpect(header().string("X-XSS-Protection", "0"));
6161
}
6262

6363
@Configuration

Diff for: config/src/test/java/org/springframework/security/config/annotation/web/configurers/HeadersConfigurerTests.java

+14-14
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ public void getWhenHeadersConfiguredThenDefaultHeadersInResponse() throws Except
8080
.andExpect(header().string(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, max-age=0, must-revalidate"))
8181
.andExpect(header().string(HttpHeaders.EXPIRES, "0"))
8282
.andExpect(header().string(HttpHeaders.PRAGMA, "no-cache"))
83-
.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "1; mode=block")).andReturn();
83+
.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "0")).andReturn();
8484
assertThat(mvcResult.getResponse().getHeaderNames()).containsExactlyInAnyOrder(
8585
HttpHeaders.X_CONTENT_TYPE_OPTIONS, HttpHeaders.X_FRAME_OPTIONS, HttpHeaders.STRICT_TRANSPORT_SECURITY,
8686
HttpHeaders.CACHE_CONTROL, HttpHeaders.EXPIRES, HttpHeaders.PRAGMA, HttpHeaders.X_XSS_PROTECTION);
@@ -97,7 +97,7 @@ public void getWhenHeadersConfiguredInLambdaThenDefaultHeadersInResponse() throw
9797
.andExpect(header().string(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, max-age=0, must-revalidate"))
9898
.andExpect(header().string(HttpHeaders.EXPIRES, "0"))
9999
.andExpect(header().string(HttpHeaders.PRAGMA, "no-cache"))
100-
.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "1; mode=block")).andReturn();
100+
.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "0")).andReturn();
101101
assertThat(mvcResult.getResponse().getHeaderNames()).containsExactlyInAnyOrder(
102102
HttpHeaders.X_CONTENT_TYPE_OPTIONS, HttpHeaders.X_FRAME_OPTIONS, HttpHeaders.STRICT_TRANSPORT_SECURITY,
103103
HttpHeaders.CACHE_CONTROL, HttpHeaders.EXPIRES, HttpHeaders.PRAGMA, HttpHeaders.X_XSS_PROTECTION);
@@ -169,33 +169,33 @@ public void getWhenHeaderDefaultsDisabledAndXssProtectionConfiguredThenOnlyXssPr
169169
throws Exception {
170170
this.spring.register(XssProtectionConfig.class).autowire();
171171
MvcResult mvcResult = this.mvc.perform(get("/").secure(true))
172-
.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "1; mode=block")).andReturn();
172+
.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "0")).andReturn();
173173
assertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.X_XSS_PROTECTION);
174174
}
175175

176176
@Test
177-
public void getWhenHeaderDefaultsDisabledAndXssProtectionConfiguredValueDisabledThenOnlyXssProtectionHeaderInResponse()
177+
public void getWhenHeaderDefaultsDisabledAndXssProtectionConfiguredEnabledModeBlockThenOnlyXssProtectionHeaderInResponse()
178178
throws Exception {
179-
this.spring.register(XssProtectionValueDisabledConfig.class).autowire();
179+
this.spring.register(XssProtectionValueEnabledModeBlockConfig.class).autowire();
180180
MvcResult mvcResult = this.mvc.perform(get("/").secure(true))
181-
.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "0")).andReturn();
181+
.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "1; mode=block")).andReturn();
182182
assertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.X_XSS_PROTECTION);
183183
}
184184

185185
@Test
186186
public void getWhenOnlyXssProtectionConfiguredInLambdaThenOnlyXssProtectionHeaderInResponse() throws Exception {
187187
this.spring.register(XssProtectionInLambdaConfig.class).autowire();
188188
MvcResult mvcResult = this.mvc.perform(get("/").secure(true))
189-
.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "1; mode=block")).andReturn();
189+
.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "0")).andReturn();
190190
assertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.X_XSS_PROTECTION);
191191
}
192192

193193
@Test
194-
public void getWhenHeaderDefaultsDisabledAndXssProtectionConfiguredValueDisabledInLambdaThenOnlyXssProtectionHeaderInResponse()
194+
public void getWhenHeaderDefaultsDisabledAndXssProtectionConfiguredValueEnabledModeBlockInLambdaThenOnlyXssProtectionHeaderInResponse()
195195
throws Exception {
196-
this.spring.register(XssProtectionValueDisabledInLambdaConfig.class).autowire();
196+
this.spring.register(XssProtectionValueEnabledModeBlockInLambdaConfig.class).autowire();
197197
MvcResult mvcResult = this.mvc.perform(get("/").secure(true))
198-
.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "0")).andReturn();
198+
.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "1; mode=block")).andReturn();
199199
assertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.X_XSS_PROTECTION);
200200
}
201201

@@ -719,7 +719,7 @@ SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
719719

720720
@Configuration
721721
@EnableWebSecurity
722-
static class XssProtectionValueDisabledConfig {
722+
static class XssProtectionValueEnabledModeBlockConfig {
723723

724724
@Bean
725725
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
@@ -728,7 +728,7 @@ SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
728728
.headers()
729729
.defaultsDisabled()
730730
.xssProtection()
731-
.headerValue(XXssProtectionHeaderWriter.HeaderValue.DISABLED);
731+
.headerValue(XXssProtectionHeaderWriter.HeaderValue.ENABLED_MODE_BLOCK);
732732
// @formatter:on
733733
return http.build();
734734
}
@@ -755,7 +755,7 @@ SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
755755

756756
@Configuration
757757
@EnableWebSecurity
758-
static class XssProtectionValueDisabledInLambdaConfig {
758+
static class XssProtectionValueEnabledModeBlockInLambdaConfig {
759759

760760
@Bean
761761
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
@@ -765,7 +765,7 @@ SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
765765
headers
766766
.defaultsDisabled()
767767
.xssProtection((xXssConfig) ->
768-
xXssConfig.headerValue(XXssProtectionHeaderWriter.HeaderValue.DISABLED)
768+
xXssConfig.headerValue(XXssProtectionHeaderWriter.HeaderValue.ENABLED_MODE_BLOCK)
769769
)
770770
);
771771
// @formatter:on

Diff for: config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpHeadersTests.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ public class NamespaceHttpHeadersTests {
6262
defaultHeaders.put("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate");
6363
defaultHeaders.put("Expires", "0");
6464
defaultHeaders.put("Pragma", "no-cache");
65-
defaultHeaders.put("X-XSS-Protection", "1; mode=block");
65+
defaultHeaders.put("X-XSS-Protection", "0");
6666
}
6767
public final SpringTestContext spring = new SpringTestContext(this);
6868

@@ -116,7 +116,7 @@ public void requestWhenXssOnlyThenBehaviorMatchesNamespace() throws Exception {
116116
@Test
117117
public void requestWhenXssCustomThenBehaviorMatchesNamespace() throws Exception {
118118
this.spring.register(XssProtectionCustomConfig.class).autowire();
119-
this.mvc.perform(get("/")).andExpect(includes(Collections.singletonMap("X-XSS-Protection", "1")));
119+
this.mvc.perform(get("/")).andExpect(includes(Collections.singletonMap("X-XSS-Protection", "1; mode=block")));
120120
}
121121

122122
@Test
@@ -291,7 +291,7 @@ SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
291291
// xss-protection@enabled and xss-protection@block
292292
.defaultsDisabled()
293293
.xssProtection()
294-
.headerValue(XXssProtectionHeaderWriter.HeaderValue.ENABLED);
294+
.headerValue(XXssProtectionHeaderWriter.HeaderValue.ENABLED_MODE_BLOCK);
295295
// @formatter:on
296296
return http.build();
297297
}

0 commit comments

Comments
 (0)