Skip to content

Commit 8ca88d1

Browse files
committed
Add X-Xss-Protection headerValue to XML config
Issue spring-projectsgh-9631
1 parent 7be2eb0 commit 8ca88d1

File tree

11 files changed

+281
-0
lines changed

11 files changed

+281
-0
lines changed

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

+10
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ public class HeadersBeanDefinitionParser implements BeanDefinitionParser {
101101

102102
private static final String ATT_POLICY_DIRECTIVES = "policy-directives";
103103

104+
private static final String ATT_HEADER_VALUE = "header-value";
105+
104106
private static final String CACHE_CONTROL_ELEMENT = "cache-control";
105107

106108
private static final String HPKP_ELEMENT = "hpkp";
@@ -595,6 +597,14 @@ private void parseXssElement(boolean addIfNotPresent, Element element, ParserCon
595597
}
596598
builder.addPropertyValue("block", block);
597599
}
600+
XXssProtectionHeaderWriter.HeaderValue headerValue = XXssProtectionHeaderWriter.HeaderValue
601+
.from(xssElt.getAttribute(ATT_HEADER_VALUE));
602+
if (headerValue != null) {
603+
if (disabled) {
604+
attrNotAllowed(parserContext, ATT_HEADER_VALUE, ATT_DISABLED, xssElt);
605+
}
606+
builder.addPropertyValue("headerValue", headerValue);
607+
}
598608
if (disabled) {
599609
return;
600610
}

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

+3
Original file line numberDiff line numberDiff line change
@@ -1298,6 +1298,9 @@ xss-protection.attlist &=
12981298
xss-protection.attlist &=
12991299
## Add mode=block to the header or not, default is on.
13001300
attribute block {xsd:boolean}?
1301+
xss-protection.attlist &=
1302+
## Specify the value for the X-Xss-Protection header. When set, overrides both enabled and block attributes.
1303+
attribute header-value {"0"|"1"|"1; mode=block"}?
13011304

13021305
content-type-options =
13031306
## Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'.

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

+14
Original file line numberDiff line numberDiff line change
@@ -3649,6 +3649,20 @@
36493649
</xs:documentation>
36503650
</xs:annotation>
36513651
</xs:attribute>
3652+
<xs:attribute name="header-value">
3653+
<xs:annotation>
3654+
<xs:documentation>Specify the value for the X-Xss-Protection header. When set, overrides both enabled and
3655+
block attributes.
3656+
</xs:documentation>
3657+
</xs:annotation>
3658+
<xs:simpleType>
3659+
<xs:restriction base="xs:token">
3660+
<xs:enumeration value="0"/>
3661+
<xs:enumeration value="1"/>
3662+
<xs:enumeration value="1\;mode=block"/>
3663+
</xs:restriction>
3664+
</xs:simpleType>
3665+
</xs:attribute>
36523666
</xs:attributeGroup>
36533667
<xs:element name="content-type-options">
36543668
<xs:annotation>

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

+59
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,58 @@ public void requestWhenDisablingXssProtectionThenDefaultsToZero() throws Excepti
384384
// @formatter:on
385385
}
386386

387+
@Test
388+
public void requestWhenSettingXssProtectionHeaderValueToZeroThenDefaultsToZero() throws Exception {
389+
Set<String> excludedHeaders = new HashSet<>(defaultHeaders.keySet());
390+
excludedHeaders.remove("X-XSS-Protection");
391+
this.spring.configLocations(this.xml("DefaultsDisabledWithXssProtectionHeaderValueZero")).autowire();
392+
// @formatter:off
393+
this.mvc.perform(get("/"))
394+
.andExpect(status().isOk())
395+
.andExpect(header().string("X-XSS-Protection", "0"))
396+
.andExpect(excludes(excludedHeaders));
397+
// @formatter:on
398+
}
399+
400+
@Test
401+
public void requestWhenSettingXssProtectionHeaderValueToOneThenDefaultsToOne() throws Exception {
402+
Set<String> excludedHeaders = new HashSet<>(defaultHeaders.keySet());
403+
excludedHeaders.remove("X-XSS-Protection");
404+
this.spring.configLocations(this.xml("DefaultsDisabledWithXssProtectionHeaderValueOne")).autowire();
405+
// @formatter:off
406+
this.mvc.perform(get("/"))
407+
.andExpect(status().isOk())
408+
.andExpect(header().string("X-XSS-Protection", "1"))
409+
.andExpect(excludes(excludedHeaders));
410+
// @formatter:on
411+
}
412+
413+
@Test
414+
public void requestWhenSettingXssProtectionHeaderValueToOneModeBlockThenDefaultsToOneModeBlock() throws Exception {
415+
Set<String> excludedHeaders = new HashSet<>(defaultHeaders.keySet());
416+
excludedHeaders.remove("X-XSS-Protection");
417+
this.spring.configLocations(this.xml("DefaultsDisabledWithXssProtectionHeaderValueOneModeBlock")).autowire();
418+
// @formatter:off
419+
this.mvc.perform(get("/"))
420+
.andExpect(status().isOk())
421+
.andExpect(header().string("X-XSS-Protection", "1; mode=block"))
422+
.andExpect(excludes(excludedHeaders));
423+
// @formatter:on
424+
}
425+
426+
@Test
427+
public void requestWhenSettingXssProtectionDisabledHeaderValueToOneThenDefaultsToOne() throws Exception {
428+
Set<String> excludedHeaders = new HashSet<>(defaultHeaders.keySet());
429+
excludedHeaders.remove("X-XSS-Protection");
430+
this.spring.configLocations(this.xml("DefaultsDisabledWithXssProtectionDisabledAndHeaderValueOne")).autowire();
431+
// @formatter:off
432+
this.mvc.perform(get("/"))
433+
.andExpect(status().isOk())
434+
.andExpect(header().string("X-XSS-Protection", "1"))
435+
.andExpect(excludes(excludedHeaders));
436+
// @formatter:on
437+
}
438+
387439
@Test
388440
public void configureWhenXssProtectionDisabledAndBlockSetThenAutowireFails() {
389441
assertThatExceptionOfType(BeanCreationException.class)
@@ -650,6 +702,13 @@ public void configureWhenXssProtectionDisabledAndBlockSpecifiedThenAutowireFails
650702
.withMessageContaining("block");
651703
}
652704

705+
@Test
706+
public void configureWhenXssProtectionDisabledAndHeaderValueSpecifiedThenAutowireFails() {
707+
assertThatExceptionOfType(BeanDefinitionParsingException.class).isThrownBy(
708+
() -> this.spring.configLocations(this.xml("XssProtectionDisabledSpecifyingHeaderValue")).autowire())
709+
.withMessageContaining("header-value");
710+
}
711+
653712
@Test
654713
public void configureWhenFrameOptionsDisabledAndPolicySpecifiedThenAutowireFails() {
655714
assertThatExceptionOfType(BeanDefinitionParsingException.class)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
~ Copyright 2002-2022 the original author or authors.
4+
~
5+
~ Licensed under the Apache License, Version 2.0 (the "License");
6+
~ you may not use this file except in compliance with the License.
7+
~ You may obtain a copy of the License at
8+
~
9+
~ https://www.apache.org/licenses/LICENSE-2.0
10+
~
11+
~ Unless required by applicable law or agreed to in writing, software
12+
~ distributed under the License is distributed on an "AS IS" BASIS,
13+
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
~ See the License for the specific language governing permissions and
15+
~ limitations under the License.
16+
-->
17+
18+
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
19+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
20+
xmlns="http://www.springframework.org/schema/security"
21+
xsi:schemaLocation="
22+
http://www.springframework.org/schema/security
23+
https://www.springframework.org/schema/security/spring-security.xsd
24+
http://www.springframework.org/schema/beans
25+
https://www.springframework.org/schema/beans/spring-beans.xsd">
26+
27+
<http auto-config="true">
28+
<headers defaults-disabled="true">
29+
<xss-protection enabled="false" header-value="1"/>
30+
</headers>
31+
</http>
32+
33+
<b:bean name="simple" class="org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController"/>
34+
35+
<b:import resource="userservice.xml"/>
36+
</b:beans>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
~ Copyright 2002-2022 the original author or authors.
4+
~
5+
~ Licensed under the Apache License, Version 2.0 (the "License");
6+
~ you may not use this file except in compliance with the License.
7+
~ You may obtain a copy of the License at
8+
~
9+
~ https://www.apache.org/licenses/LICENSE-2.0
10+
~
11+
~ Unless required by applicable law or agreed to in writing, software
12+
~ distributed under the License is distributed on an "AS IS" BASIS,
13+
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
~ See the License for the specific language governing permissions and
15+
~ limitations under the License.
16+
-->
17+
18+
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
19+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
20+
xmlns="http://www.springframework.org/schema/security"
21+
xsi:schemaLocation="
22+
http://www.springframework.org/schema/security
23+
https://www.springframework.org/schema/security/spring-security.xsd
24+
http://www.springframework.org/schema/beans
25+
https://www.springframework.org/schema/beans/spring-beans.xsd">
26+
27+
<http auto-config="true">
28+
<headers defaults-disabled="true">
29+
<xss-protection header-value="1"/>
30+
</headers>
31+
</http>
32+
33+
<b:bean name="simple" class="org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController"/>
34+
35+
<b:import resource="userservice.xml"/>
36+
</b:beans>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
~ Copyright 2002-2022 the original author or authors.
4+
~
5+
~ Licensed under the Apache License, Version 2.0 (the "License");
6+
~ you may not use this file except in compliance with the License.
7+
~ You may obtain a copy of the License at
8+
~
9+
~ https://www.apache.org/licenses/LICENSE-2.0
10+
~
11+
~ Unless required by applicable law or agreed to in writing, software
12+
~ distributed under the License is distributed on an "AS IS" BASIS,
13+
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
~ See the License for the specific language governing permissions and
15+
~ limitations under the License.
16+
-->
17+
18+
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
19+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
20+
xmlns="http://www.springframework.org/schema/security"
21+
xsi:schemaLocation="
22+
http://www.springframework.org/schema/security
23+
https://www.springframework.org/schema/security/spring-security.xsd
24+
http://www.springframework.org/schema/beans
25+
https://www.springframework.org/schema/beans/spring-beans.xsd">
26+
27+
<http auto-config="true">
28+
<headers defaults-disabled="true">
29+
<xss-protection header-value="1; mode=block"/>
30+
</headers>
31+
</http>
32+
33+
<b:bean name="simple" class="org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController"/>
34+
35+
<b:import resource="userservice.xml"/>
36+
</b:beans>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
~ Copyright 2002-2022 the original author or authors.
4+
~
5+
~ Licensed under the Apache License, Version 2.0 (the "License");
6+
~ you may not use this file except in compliance with the License.
7+
~ You may obtain a copy of the License at
8+
~
9+
~ https://www.apache.org/licenses/LICENSE-2.0
10+
~
11+
~ Unless required by applicable law or agreed to in writing, software
12+
~ distributed under the License is distributed on an "AS IS" BASIS,
13+
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
~ See the License for the specific language governing permissions and
15+
~ limitations under the License.
16+
-->
17+
18+
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
19+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
20+
xmlns="http://www.springframework.org/schema/security"
21+
xsi:schemaLocation="
22+
http://www.springframework.org/schema/security
23+
https://www.springframework.org/schema/security/spring-security.xsd
24+
http://www.springframework.org/schema/beans
25+
https://www.springframework.org/schema/beans/spring-beans.xsd">
26+
27+
<http auto-config="true">
28+
<headers defaults-disabled="true">
29+
<xss-protection header-value="0"/>
30+
</headers>
31+
</http>
32+
33+
<b:bean name="simple" class="org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController"/>
34+
35+
<b:import resource="userservice.xml"/>
36+
</b:beans>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
~ Copyright 2002-2022 the original author or authors.
4+
~
5+
~ Licensed under the Apache License, Version 2.0 (the "License");
6+
~ you may not use this file except in compliance with the License.
7+
~ You may obtain a copy of the License at
8+
~
9+
~ https://www.apache.org/licenses/LICENSE-2.0
10+
~
11+
~ Unless required by applicable law or agreed to in writing, software
12+
~ distributed under the License is distributed on an "AS IS" BASIS,
13+
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
~ See the License for the specific language governing permissions and
15+
~ limitations under the License.
16+
-->
17+
18+
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
19+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
20+
xmlns="http://www.springframework.org/schema/security"
21+
xsi:schemaLocation="
22+
http://www.springframework.org/schema/security
23+
https://www.springframework.org/schema/security/spring-security.xsd
24+
http://www.springframework.org/schema/beans
25+
https://www.springframework.org/schema/beans/spring-beans.xsd">
26+
27+
<http auto-config="true">
28+
<headers>
29+
<xss-protection disabled="true" header-value="1"/>
30+
</headers>
31+
</http>
32+
33+
<b:bean name="simple" class="org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController"/>
34+
35+
<b:import resource="userservice.xml"/>
36+
</b:beans>

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

+6
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,12 @@ This indicates to the browser that the page should not be loaded at all.
578578
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.
579579
Note that there are sometimes ways of bypassing this mode which can often times make blocking the page more desirable.
580580

581+
[[nsa-xss-protection-header-value]]
582+
* **xss-protection-header-value**
583+
Explicitly set the value for https://en.wikipedia.org/wiki/Cross-site_scripting#Non-Persistent[reflected / Type-1 Cross-Site Scripting (XSS)] header.
584+
One of: "0", "1", "1; mode=block".
585+
When set, overrides both enabled and block attributes.
586+
581587

582588
[[nsa-xss-protection-parents]]
583589
=== Parent Elements of <xss-protection>

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

+9
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,15 @@ public enum HeaderValue {
149149
this.value = value;
150150
}
151151

152+
public static HeaderValue from(String headerValue) {
153+
for (HeaderValue value : values()) {
154+
if (value.toString().equals(headerValue)) {
155+
return value;
156+
}
157+
}
158+
return null;
159+
}
160+
152161
@Override
153162
public String toString() {
154163
return this.value;

0 commit comments

Comments
 (0)