Skip to content

Commit 23294c4

Browse files
eddumelendezRob Winch
authored andcommitted
Add Referrer-Policy header support
Fixes gh-4110
1 parent eb2870b commit 23294c4

11 files changed

Lines changed: 475 additions & 3 deletions

File tree

config/src/main/java/org/springframework/security/config/SecurityNamespaceHandler.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ private boolean namespaceMatchesVersion(Element element) {
221221
private boolean matchesVersionInternal(Element element) {
222222
String schemaLocation = element.getAttributeNS(
223223
"http://www.w3.org/2001/XMLSchema-instance", "schemaLocation");
224-
return schemaLocation.matches("(?m).*spring-security-4\\.1.*.xsd.*")
224+
return schemaLocation.matches("(?m).*spring-security-4\\.2.*.xsd.*")
225225
|| schemaLocation.matches("(?m).*spring-security.xsd.*")
226226
|| !schemaLocation.matches("(?m).*spring-security.*");
227227
}

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

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.springframework.security.web.header.HeaderWriter;
2929
import org.springframework.security.web.header.HeaderWriterFilter;
3030
import org.springframework.security.web.header.writers.*;
31+
import org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter.ReferrerPolicy;
3132
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter;
3233
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter.XFrameOptionsMode;
3334
import org.springframework.security.web.util.matcher.RequestMatcher;
@@ -56,6 +57,7 @@
5657
* @author Rob Winch
5758
* @author Tim Ysewyn
5859
* @author Joe Grandja
60+
* @author Eddú Meléndez
5961
* @since 3.2
6062
*/
6163
public class HeadersConfigurer<H extends HttpSecurityBuilder<H>> extends
@@ -78,6 +80,8 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>> extends
7880

7981
private final ContentSecurityPolicyConfig contentSecurityPolicy = new ContentSecurityPolicyConfig();
8082

83+
private final ReferrerPolicyConfig referrerPolicy = new ReferrerPolicyConfig();
84+
8185
/**
8286
* Creates a new instance
8387
*
@@ -770,6 +774,7 @@ private List<HeaderWriter> getHeaderWriters() {
770774
addIfNotNull(writers, frameOptions.writer);
771775
addIfNotNull(writers, hpkp.writer);
772776
addIfNotNull(writers, contentSecurityPolicy.writer);
777+
addIfNotNull(writers, referrerPolicy.writer);
773778
writers.addAll(headerWriters);
774779
return writers;
775780
}
@@ -779,4 +784,68 @@ private <T> void addIfNotNull(List<T> values, T value) {
779784
values.add(value);
780785
}
781786
}
787+
788+
/**
789+
* <p>
790+
* Allows configuration for <a href="https://www.w3.org/TR/referrer-policy/">Referrer Policy</a>.
791+
* </p>
792+
*
793+
* <p>
794+
* Configuration is provided to the {@link ReferrerPolicyHeaderWriter} which support the writing
795+
* of the header as detailed in the W3C Technical Report:
796+
* </p>
797+
* <ul>
798+
* <li>Referrer-Policy</li>
799+
* </ul>
800+
*
801+
* <p>Default value is:</p>
802+
*
803+
* <pre>
804+
* Referrer-Policy: no-referrer
805+
* </pre>
806+
*
807+
* @see ReferrerPolicyHeaderWriter
808+
* @since 4.2
809+
* @return the ReferrerPolicyConfig for additional configuration
810+
*/
811+
public ReferrerPolicyConfig referrerPolicy() {
812+
this.referrerPolicy.writer = new ReferrerPolicyHeaderWriter();
813+
return this.referrerPolicy;
814+
}
815+
816+
/**
817+
* <p>
818+
* Allows configuration for <a href="https://www.w3.org/TR/referrer-policy/">Referrer Policy</a>.
819+
* </p>
820+
*
821+
* <p>
822+
* Configuration is provided to the {@link ReferrerPolicyHeaderWriter} which support the writing
823+
* of the header as detailed in the W3C Technical Report:
824+
* </p>
825+
* <ul>
826+
* <li>Referrer-Policy</li>
827+
* </ul>
828+
*
829+
* @see ReferrerPolicyHeaderWriter
830+
* @since 4.2
831+
* @return the ReferrerPolicyConfig for additional configuration
832+
* @throws IllegalArgumentException if policy is null or empty
833+
*/
834+
public ReferrerPolicyConfig referrerPolicy(ReferrerPolicy policy) {
835+
this.referrerPolicy.writer = new ReferrerPolicyHeaderWriter(policy);
836+
return this.referrerPolicy;
837+
}
838+
839+
public final class ReferrerPolicyConfig {
840+
841+
private ReferrerPolicyHeaderWriter writer;
842+
843+
private ReferrerPolicyConfig() {
844+
}
845+
846+
public HeadersConfigurer<H> and() {
847+
return HeadersConfigurer.this;
848+
}
849+
850+
}
782851
}

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.springframework.beans.factory.xml.ParserContext;
3232
import org.springframework.security.web.header.HeaderWriterFilter;
3333
import org.springframework.security.web.header.writers.*;
34+
import org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter.ReferrerPolicy;
3435
import org.springframework.security.web.header.writers.frameoptions.RegExpAllowFromStrategy;
3536
import org.springframework.security.web.header.writers.frameoptions.StaticAllowFromStrategy;
3637
import org.springframework.security.web.header.writers.frameoptions.WhiteListedAllowFromStrategy;
@@ -45,6 +46,7 @@
4546
*
4647
* @author Marten Deinum
4748
* @author Tim Ysewyn
49+
* @author Eddú Meléndez
4850
* @since 3.2
4951
*/
5052
public class HeadersBeanDefinitionParser implements BeanDefinitionParser {
@@ -82,6 +84,7 @@ public class HeadersBeanDefinitionParser implements BeanDefinitionParser {
8284
private static final String GENERIC_HEADER_ELEMENT = "header";
8385

8486
private static final String CONTENT_SECURITY_POLICY_ELEMENT = "content-security-policy";
87+
private static final String REFERRER_POLICY_ELEMENT = "referrer-policy";
8588

8689
private static final String ALLOW_FROM = "ALLOW-FROM";
8790

@@ -109,6 +112,8 @@ public BeanDefinition parse(Element element, ParserContext parserContext) {
109112

110113
parseContentSecurityPolicyElement(disabled, element, parserContext);
111114

115+
parseReferrerPolicyElement(element, parserContext);
116+
112117
parseHeaderElements(element);
113118

114119
boolean noWriters = headerWriters.isEmpty();
@@ -291,6 +296,23 @@ private void addContentSecurityPolicy(Element contentSecurityPolicyElement, Pars
291296
headerWriters.add(headersWriter.getBeanDefinition());
292297
}
293298

299+
private void parseReferrerPolicyElement(Element element, ParserContext context) {
300+
Element referrerPolicyElement = (element == null) ? null : DomUtils.getChildElementByTagName(element, REFERRER_POLICY_ELEMENT);
301+
if (referrerPolicyElement != null) {
302+
addReferrerPolicy(referrerPolicyElement, context);
303+
}
304+
}
305+
306+
private void addReferrerPolicy(Element referrerPolicyElement, ParserContext context) {
307+
BeanDefinitionBuilder headersWriter = BeanDefinitionBuilder.genericBeanDefinition(ReferrerPolicyHeaderWriter.class);
308+
309+
String policy = referrerPolicyElement.getAttribute(ATT_POLICY);
310+
if (StringUtils.hasLength(policy)) {
311+
headersWriter.addConstructorArgValue(ReferrerPolicy.get(policy));
312+
}
313+
headerWriters.add(headersWriter.getBeanDefinition());
314+
}
315+
294316
private void attrNotAllowed(ParserContext context, String attrName,
295317
String otherAttrName, Element element) {
296318
context.getReaderContext().error(

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -753,7 +753,7 @@ csrf-options.attlist &=
753753

754754
headers =
755755
## Element for configuration of the HeaderWritersFilter. Enables easy setting for the X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.
756-
element headers { headers-options.attlist, (cache-control? & xss-protection? & hsts? & frame-options? & content-type-options? & hpkp? & content-security-policy? & header*)}
756+
element headers { headers-options.attlist, (cache-control? & xss-protection? & hsts? & frame-options? & content-type-options? & hpkp? & content-security-policy? & referrer-policy? & header*)}
757757
headers-options.attlist &=
758758
## Specifies if the default headers should be disabled. Default false.
759759
attribute defaults-disabled {xsd:boolean}?
@@ -824,6 +824,13 @@ csp-options.attlist &=
824824
## Set to true, to enable the Content-Security-Policy-Report-Only header for reporting policy violations only. Defaults to false.
825825
attribute report-only {xsd:boolean}?
826826

827+
referrer-policy =
828+
## Adds support for Referrer Policy
829+
element referrer-policy {referrer-options.attlist}
830+
referrer-options.attlist &=
831+
## The policies for the Referrer-Policy header.
832+
attribute policy {"no-referrer","no-referrer-when-downgrade","same-origin","origin","strict-origin","origin-when-cross-origin","strict-origin-when-cross-origin","unsafe-url"}?
833+
827834
cache-control =
828835
## Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for every request
829836
element cache-control {cache-control.attlist}

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2357,6 +2357,7 @@
23572357
<xs:element ref="security:content-type-options"/>
23582358
<xs:element ref="security:hpkp"/>
23592359
<xs:element ref="security:content-security-policy"/>
2360+
<xs:element ref="security:referrer-policy"/>
23602361
<xs:element ref="security:header"/>
23612362
</xs:choice>
23622363
<xs:attributeGroup ref="security:headers-options.attlist"/>
@@ -2539,6 +2540,35 @@
25392540
</xs:annotation>
25402541
</xs:attribute>
25412542
</xs:attributeGroup>
2543+
<xs:element name="referrer-policy">
2544+
<xs:annotation>
2545+
<xs:documentation>Adds support for Referrer Policy
2546+
</xs:documentation>
2547+
</xs:annotation>
2548+
<xs:complexType>
2549+
<xs:attributeGroup ref="security:referrer-options.attlist"/>
2550+
</xs:complexType>
2551+
</xs:element>
2552+
<xs:attributeGroup name="referrer-options.attlist">
2553+
<xs:attribute name="policy">
2554+
<xs:annotation>
2555+
<xs:documentation>The policies for the Referrer-Policy header.
2556+
</xs:documentation>
2557+
</xs:annotation>
2558+
<xs:simpleType>
2559+
<xs:restriction base="xs:token">
2560+
<xs:enumeration value="no-referrer"/>
2561+
<xs:enumeration value="no-referrer-when-downgrade"/>
2562+
<xs:enumeration value="same-origin"/>
2563+
<xs:enumeration value="origin"/>
2564+
<xs:enumeration value="strict-origin"/>
2565+
<xs:enumeration value="origin-when-cross-origin"/>
2566+
<xs:enumeration value="strict-origin-when-cross-origin"/>
2567+
<xs:enumeration value="unsafe-url"/>
2568+
</xs:restriction>
2569+
</xs:simpleType>
2570+
</xs:attribute>
2571+
</xs:attributeGroup>
25422572
<xs:element name="cache-control">
25432573
<xs:annotation>
25442574
<xs:documentation>Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for

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

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,14 @@ import org.springframework.security.config.annotation.BaseSpringSpec
2020
import org.springframework.security.config.annotation.web.builders.HttpSecurity
2121
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
2222
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
23+
import static org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter.ReferrerPolicy
2324

2425
/**
2526
*
2627
* @author Rob Winch
2728
* @author Tim Ysewyn
2829
* @author Joe Grandja
30+
* @author Eddú Meléndez
2931
*/
3032
class HeadersConfigurerTests extends BaseSpringSpec {
3133

@@ -453,4 +455,46 @@ class HeadersConfigurerTests extends BaseSpringSpec {
453455
}
454456
}
455457

458+
def "headers.referrerPolicy default"() {
459+
setup:
460+
loadConfig(ReferrerPolicyDefaultConfig)
461+
when:
462+
springSecurityFilterChain.doFilter(request,response,chain)
463+
then:
464+
responseHeaders == ['Referrer-Policy': 'no-referrer']
465+
}
466+
467+
@EnableWebSecurity
468+
static class ReferrerPolicyDefaultConfig extends WebSecurityConfigurerAdapter {
469+
470+
@Override
471+
protected void configure(HttpSecurity http) throws Exception {
472+
http
473+
.headers()
474+
.defaultsDisabled()
475+
.referrerPolicy();
476+
}
477+
}
478+
479+
def "headers.referrerPolicy custom"() {
480+
setup:
481+
loadConfig(ReferrerPolicyCustomConfig)
482+
when:
483+
springSecurityFilterChain.doFilter(request,response,chain)
484+
then:
485+
responseHeaders == ['Referrer-Policy': 'same-origin']
486+
}
487+
488+
@EnableWebSecurity
489+
static class ReferrerPolicyCustomConfig extends WebSecurityConfigurerAdapter {
490+
491+
@Override
492+
protected void configure(HttpSecurity http) throws Exception {
493+
http
494+
.headers()
495+
.defaultsDisabled()
496+
.referrerPolicy(ReferrerPolicy.SAME_ORIGIN);
497+
}
498+
}
499+
456500
}

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -920,6 +920,38 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
920920
assertHeaders(response, expectedHeaders)
921921
}
922922

923+
def 'http headers defaults : referrer-policy'() {
924+
setup:
925+
httpAutoConfig {
926+
'headers'('defaults-disabled':true) {
927+
'referrer-policy'()
928+
}
929+
}
930+
createAppContext()
931+
when:
932+
def hf = getFilter(HeaderWriterFilter)
933+
MockHttpServletResponse response = new MockHttpServletResponse()
934+
hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain())
935+
then:
936+
assertHeaders(response, ['Referrer-Policy': 'no-referrer'])
937+
}
938+
939+
def 'http headers defaults : referrer-policy same-origin'() {
940+
setup:
941+
httpAutoConfig {
942+
'headers'('defaults-disabled':true) {
943+
'referrer-policy'('policy': 'same-origin')
944+
}
945+
}
946+
createAppContext()
947+
when:
948+
def hf = getFilter(HeaderWriterFilter)
949+
MockHttpServletResponse response = new MockHttpServletResponse()
950+
hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain())
951+
then:
952+
assertHeaders(response, ['Referrer-Policy': 'same-origin'])
953+
}
954+
923955
def assertHeaders(MockHttpServletResponse response, Map<String,String> expected) {
924956
assert response.headerNames == expected.keySet()
925957
expected.each { headerName, value ->

config/src/test/java/org/springframework/security/config/util/InMemoryXmlApplicationContext.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public class InMemoryXmlApplicationContext extends AbstractXmlApplicationContext
4040
+ "http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-";
4141
static final String BEANS_CLOSE = "</b:beans>\n";
4242

43-
static final String SPRING_SECURITY_VERSION = "4.1";
43+
static final String SPRING_SECURITY_VERSION = "4.2";
4444

4545
Resource inMemoryXml;
4646

0 commit comments

Comments
 (0)