Add Referrer-Policy header support
Fixes gh-4110
This commit is contained in:
+1
-1
@@ -221,7 +221,7 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
|
||||
private boolean matchesVersionInternal(Element element) {
|
||||
String schemaLocation = element.getAttributeNS(
|
||||
"http://www.w3.org/2001/XMLSchema-instance", "schemaLocation");
|
||||
return schemaLocation.matches("(?m).*spring-security-4\\.1.*.xsd.*")
|
||||
return schemaLocation.matches("(?m).*spring-security-4\\.2.*.xsd.*")
|
||||
|| schemaLocation.matches("(?m).*spring-security.xsd.*")
|
||||
|| !schemaLocation.matches("(?m).*spring-security.*");
|
||||
}
|
||||
|
||||
+69
@@ -28,6 +28,7 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
|
||||
import org.springframework.security.web.header.HeaderWriter;
|
||||
import org.springframework.security.web.header.HeaderWriterFilter;
|
||||
import org.springframework.security.web.header.writers.*;
|
||||
import org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter.ReferrerPolicy;
|
||||
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter;
|
||||
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter.XFrameOptionsMode;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
@@ -56,6 +57,7 @@ import org.springframework.util.Assert;
|
||||
* @author Rob Winch
|
||||
* @author Tim Ysewyn
|
||||
* @author Joe Grandja
|
||||
* @author Eddú Meléndez
|
||||
* @since 3.2
|
||||
*/
|
||||
public class HeadersConfigurer<H extends HttpSecurityBuilder<H>> extends
|
||||
@@ -78,6 +80,8 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>> extends
|
||||
|
||||
private final ContentSecurityPolicyConfig contentSecurityPolicy = new ContentSecurityPolicyConfig();
|
||||
|
||||
private final ReferrerPolicyConfig referrerPolicy = new ReferrerPolicyConfig();
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
*
|
||||
@@ -770,6 +774,7 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>> extends
|
||||
addIfNotNull(writers, frameOptions.writer);
|
||||
addIfNotNull(writers, hpkp.writer);
|
||||
addIfNotNull(writers, contentSecurityPolicy.writer);
|
||||
addIfNotNull(writers, referrerPolicy.writer);
|
||||
writers.addAll(headerWriters);
|
||||
return writers;
|
||||
}
|
||||
@@ -779,4 +784,68 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>> extends
|
||||
values.add(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Allows configuration for <a href="https://www.w3.org/TR/referrer-policy/">Referrer Policy</a>.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Configuration is provided to the {@link ReferrerPolicyHeaderWriter} which support the writing
|
||||
* of the header as detailed in the W3C Technical Report:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>Referrer-Policy</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Default value is:</p>
|
||||
*
|
||||
* <pre>
|
||||
* Referrer-Policy: no-referrer
|
||||
* </pre>
|
||||
*
|
||||
* @see ReferrerPolicyHeaderWriter
|
||||
* @since 4.2
|
||||
* @return the ReferrerPolicyConfig for additional configuration
|
||||
*/
|
||||
public ReferrerPolicyConfig referrerPolicy() {
|
||||
this.referrerPolicy.writer = new ReferrerPolicyHeaderWriter();
|
||||
return this.referrerPolicy;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Allows configuration for <a href="https://www.w3.org/TR/referrer-policy/">Referrer Policy</a>.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Configuration is provided to the {@link ReferrerPolicyHeaderWriter} which support the writing
|
||||
* of the header as detailed in the W3C Technical Report:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>Referrer-Policy</li>
|
||||
* </ul>
|
||||
*
|
||||
* @see ReferrerPolicyHeaderWriter
|
||||
* @since 4.2
|
||||
* @return the ReferrerPolicyConfig for additional configuration
|
||||
* @throws IllegalArgumentException if policy is null or empty
|
||||
*/
|
||||
public ReferrerPolicyConfig referrerPolicy(ReferrerPolicy policy) {
|
||||
this.referrerPolicy.writer = new ReferrerPolicyHeaderWriter(policy);
|
||||
return this.referrerPolicy;
|
||||
}
|
||||
|
||||
public final class ReferrerPolicyConfig {
|
||||
|
||||
private ReferrerPolicyHeaderWriter writer;
|
||||
|
||||
private ReferrerPolicyConfig() {
|
||||
}
|
||||
|
||||
public HeadersConfigurer<H> and() {
|
||||
return HeadersConfigurer.this;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+22
@@ -31,6 +31,7 @@ import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.security.web.header.HeaderWriterFilter;
|
||||
import org.springframework.security.web.header.writers.*;
|
||||
import org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter.ReferrerPolicy;
|
||||
import org.springframework.security.web.header.writers.frameoptions.RegExpAllowFromStrategy;
|
||||
import org.springframework.security.web.header.writers.frameoptions.StaticAllowFromStrategy;
|
||||
import org.springframework.security.web.header.writers.frameoptions.WhiteListedAllowFromStrategy;
|
||||
@@ -45,6 +46,7 @@ import org.w3c.dom.Node;
|
||||
*
|
||||
* @author Marten Deinum
|
||||
* @author Tim Ysewyn
|
||||
* @author Eddú Meléndez
|
||||
* @since 3.2
|
||||
*/
|
||||
public class HeadersBeanDefinitionParser implements BeanDefinitionParser {
|
||||
@@ -82,6 +84,7 @@ public class HeadersBeanDefinitionParser implements BeanDefinitionParser {
|
||||
private static final String GENERIC_HEADER_ELEMENT = "header";
|
||||
|
||||
private static final String CONTENT_SECURITY_POLICY_ELEMENT = "content-security-policy";
|
||||
private static final String REFERRER_POLICY_ELEMENT = "referrer-policy";
|
||||
|
||||
private static final String ALLOW_FROM = "ALLOW-FROM";
|
||||
|
||||
@@ -109,6 +112,8 @@ public class HeadersBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
parseContentSecurityPolicyElement(disabled, element, parserContext);
|
||||
|
||||
parseReferrerPolicyElement(element, parserContext);
|
||||
|
||||
parseHeaderElements(element);
|
||||
|
||||
boolean noWriters = headerWriters.isEmpty();
|
||||
@@ -291,6 +296,23 @@ public class HeadersBeanDefinitionParser implements BeanDefinitionParser {
|
||||
headerWriters.add(headersWriter.getBeanDefinition());
|
||||
}
|
||||
|
||||
private void parseReferrerPolicyElement(Element element, ParserContext context) {
|
||||
Element referrerPolicyElement = (element == null) ? null : DomUtils.getChildElementByTagName(element, REFERRER_POLICY_ELEMENT);
|
||||
if (referrerPolicyElement != null) {
|
||||
addReferrerPolicy(referrerPolicyElement, context);
|
||||
}
|
||||
}
|
||||
|
||||
private void addReferrerPolicy(Element referrerPolicyElement, ParserContext context) {
|
||||
BeanDefinitionBuilder headersWriter = BeanDefinitionBuilder.genericBeanDefinition(ReferrerPolicyHeaderWriter.class);
|
||||
|
||||
String policy = referrerPolicyElement.getAttribute(ATT_POLICY);
|
||||
if (StringUtils.hasLength(policy)) {
|
||||
headersWriter.addConstructorArgValue(ReferrerPolicy.get(policy));
|
||||
}
|
||||
headerWriters.add(headersWriter.getBeanDefinition());
|
||||
}
|
||||
|
||||
private void attrNotAllowed(ParserContext context, String attrName,
|
||||
String otherAttrName, Element element) {
|
||||
context.getReaderContext().error(
|
||||
|
||||
+8
-1
@@ -753,7 +753,7 @@ csrf-options.attlist &=
|
||||
|
||||
headers =
|
||||
## Element for configuration of the HeaderWritersFilter. Enables easy setting for the X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.
|
||||
element headers { headers-options.attlist, (cache-control? & xss-protection? & hsts? & frame-options? & content-type-options? & hpkp? & content-security-policy? & header*)}
|
||||
element headers { headers-options.attlist, (cache-control? & xss-protection? & hsts? & frame-options? & content-type-options? & hpkp? & content-security-policy? & referrer-policy? & header*)}
|
||||
headers-options.attlist &=
|
||||
## Specifies if the default headers should be disabled. Default false.
|
||||
attribute defaults-disabled {xsd:boolean}?
|
||||
@@ -824,6 +824,13 @@ csp-options.attlist &=
|
||||
## Set to true, to enable the Content-Security-Policy-Report-Only header for reporting policy violations only. Defaults to false.
|
||||
attribute report-only {xsd:boolean}?
|
||||
|
||||
referrer-policy =
|
||||
## Adds support for Referrer Policy
|
||||
element referrer-policy {referrer-options.attlist}
|
||||
referrer-options.attlist &=
|
||||
## The policies for the Referrer-Policy header.
|
||||
attribute policy {"no-referrer","no-referrer-when-downgrade","same-origin","origin","strict-origin","origin-when-cross-origin","strict-origin-when-cross-origin","unsafe-url"}?
|
||||
|
||||
cache-control =
|
||||
## Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for every request
|
||||
element cache-control {cache-control.attlist}
|
||||
|
||||
@@ -2357,6 +2357,7 @@
|
||||
<xs:element ref="security:content-type-options"/>
|
||||
<xs:element ref="security:hpkp"/>
|
||||
<xs:element ref="security:content-security-policy"/>
|
||||
<xs:element ref="security:referrer-policy"/>
|
||||
<xs:element ref="security:header"/>
|
||||
</xs:choice>
|
||||
<xs:attributeGroup ref="security:headers-options.attlist"/>
|
||||
@@ -2539,6 +2540,35 @@
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:attributeGroup>
|
||||
<xs:element name="referrer-policy">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Adds support for Referrer Policy
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:attributeGroup ref="security:referrer-options.attlist"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:attributeGroup name="referrer-options.attlist">
|
||||
<xs:attribute name="policy">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The policies for the Referrer-Policy header.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:token">
|
||||
<xs:enumeration value="no-referrer"/>
|
||||
<xs:enumeration value="no-referrer-when-downgrade"/>
|
||||
<xs:enumeration value="same-origin"/>
|
||||
<xs:enumeration value="origin"/>
|
||||
<xs:enumeration value="strict-origin"/>
|
||||
<xs:enumeration value="origin-when-cross-origin"/>
|
||||
<xs:enumeration value="strict-origin-when-cross-origin"/>
|
||||
<xs:enumeration value="unsafe-url"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
</xs:attributeGroup>
|
||||
<xs:element name="cache-control">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for
|
||||
|
||||
+44
@@ -20,12 +20,14 @@ import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
|
||||
import static org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter.ReferrerPolicy
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Tim Ysewyn
|
||||
* @author Joe Grandja
|
||||
* @author Eddú Meléndez
|
||||
*/
|
||||
class HeadersConfigurerTests extends BaseSpringSpec {
|
||||
|
||||
@@ -453,4 +455,46 @@ class HeadersConfigurerTests extends BaseSpringSpec {
|
||||
}
|
||||
}
|
||||
|
||||
def "headers.referrerPolicy default"() {
|
||||
setup:
|
||||
loadConfig(ReferrerPolicyDefaultConfig)
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
responseHeaders == ['Referrer-Policy': 'no-referrer']
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class ReferrerPolicyDefaultConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.headers()
|
||||
.defaultsDisabled()
|
||||
.referrerPolicy();
|
||||
}
|
||||
}
|
||||
|
||||
def "headers.referrerPolicy custom"() {
|
||||
setup:
|
||||
loadConfig(ReferrerPolicyCustomConfig)
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
responseHeaders == ['Referrer-Policy': 'same-origin']
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class ReferrerPolicyCustomConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.headers()
|
||||
.defaultsDisabled()
|
||||
.referrerPolicy(ReferrerPolicy.SAME_ORIGIN);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+32
@@ -920,6 +920,38 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
|
||||
assertHeaders(response, expectedHeaders)
|
||||
}
|
||||
|
||||
def 'http headers defaults : referrer-policy'() {
|
||||
setup:
|
||||
httpAutoConfig {
|
||||
'headers'('defaults-disabled':true) {
|
||||
'referrer-policy'()
|
||||
}
|
||||
}
|
||||
createAppContext()
|
||||
when:
|
||||
def hf = getFilter(HeaderWriterFilter)
|
||||
MockHttpServletResponse response = new MockHttpServletResponse()
|
||||
hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain())
|
||||
then:
|
||||
assertHeaders(response, ['Referrer-Policy': 'no-referrer'])
|
||||
}
|
||||
|
||||
def 'http headers defaults : referrer-policy same-origin'() {
|
||||
setup:
|
||||
httpAutoConfig {
|
||||
'headers'('defaults-disabled':true) {
|
||||
'referrer-policy'('policy': 'same-origin')
|
||||
}
|
||||
}
|
||||
createAppContext()
|
||||
when:
|
||||
def hf = getFilter(HeaderWriterFilter)
|
||||
MockHttpServletResponse response = new MockHttpServletResponse()
|
||||
hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain())
|
||||
then:
|
||||
assertHeaders(response, ['Referrer-Policy': 'same-origin'])
|
||||
}
|
||||
|
||||
def assertHeaders(MockHttpServletResponse response, Map<String,String> expected) {
|
||||
assert response.headerNames == expected.keySet()
|
||||
expected.each { headerName, value ->
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ public class InMemoryXmlApplicationContext extends AbstractXmlApplicationContext
|
||||
+ "http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-";
|
||||
static final String BEANS_CLOSE = "</b:beans>\n";
|
||||
|
||||
static final String SPRING_SECURITY_VERSION = "4.1";
|
||||
static final String SPRING_SECURITY_VERSION = "4.2";
|
||||
|
||||
Resource inMemoryXml;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user