Add properties for content security policy

Add `content-security-policy` and `content-security-policy-mode`
`security.header` properties and update auto-configuration to apply
them when specified.

Fixes gh-7373
Closes gh-7357
This commit is contained in:
Christoffer Sawicki 2016-11-12 20:36:54 +01:00 committed by Phillip Webb
parent de26b22fa4
commit d7bbea63b7
4 changed files with 82 additions and 1 deletions

View File

@ -174,6 +174,18 @@ public class SecurityProperties implements SecurityPrerequisite {
NONE, DOMAIN, ALL
}
public enum ContentSecurityPolicyMode {
/**
* Use the {@code Content-Security-Policy} header.
*/
DEFAULT,
/**
* Use the {@code Content-Security-Policy-Report-Only} header.
*/
REPORT_ONLY
}
/**
* Enable cross site scripting (XSS) protection.
*/
@ -194,6 +206,16 @@ public class SecurityProperties implements SecurityPrerequisite {
*/
private boolean contentType = true;
/**
* Value for content security policy header.
*/
private String contentSecurityPolicy;
/**
* Whether to use the "Content-Security-Policy" or "Content-Security-Policy-Report-Only" header.
*/
private ContentSecurityPolicyMode contentSecurityPolicyMode = ContentSecurityPolicyMode.DEFAULT;
/**
* HTTP Strict Transport Security (HSTS) mode (none, domain, all).
*/
@ -231,6 +253,22 @@ public class SecurityProperties implements SecurityPrerequisite {
this.contentType = contentType;
}
public String getContentSecurityPolicy() {
return this.contentSecurityPolicy;
}
public void setContentSecurityPolicy(String contentSecurityPolicy) {
this.contentSecurityPolicy = contentSecurityPolicy;
}
public ContentSecurityPolicyMode getContentSecurityPolicyMode() {
return this.contentSecurityPolicyMode;
}
public void setContentSecurityPolicyMode(ContentSecurityPolicyMode contentSecurityPolicyMode) {
this.contentSecurityPolicyMode = contentSecurityPolicyMode;
}
public HSTS getHsts() {
return this.hsts;
}

View File

@ -108,6 +108,15 @@ public class SpringBootWebSecurityConfiguration {
if (!headers.isContentType()) {
configurer.contentTypeOptions().disable();
}
if (StringUtils.hasText(headers.getContentSecurityPolicy())) {
if (headers.getContentSecurityPolicyMode() == Headers.ContentSecurityPolicyMode.DEFAULT) {
configurer.contentSecurityPolicy(headers.getContentSecurityPolicy());
}
else {
assert headers.getContentSecurityPolicyMode() == Headers.ContentSecurityPolicyMode.REPORT_ONLY;
configurer.contentSecurityPolicy(headers.getContentSecurityPolicy()).reportOnly();
}
}
if (!headers.isXss()) {
configurer.xssProtection().disable();
}

View File

@ -213,7 +213,8 @@ public class SpringBootWebSecurityConfigurationTests {
.andExpect(MockMvcResultMatchers.header().string("Cache-Control",
is(notNullValue())))
.andExpect(MockMvcResultMatchers.header().string("X-Frame-Options",
is(notNullValue())));
is(notNullValue())))
.andExpect(MockMvcResultMatchers.header().doesNotExist("Content-Security-Policy"));
}
@Test
@ -239,6 +240,37 @@ public class SpringBootWebSecurityConfigurationTests {
MockMvcResultMatchers.header().doesNotExist("X-Frame-Options"));
}
@Test
public void contentSecurityPolicyConfiguration() throws Exception {
this.context = SpringApplication.run(VanillaWebConfiguration.class,
"--security.headers.content-security-policy=default-src 'self';");
MockMvc mockMvc = MockMvcBuilders
.webAppContextSetup((WebApplicationContext) this.context)
.addFilters((FilterChainProxy) this.context
.getBean("springSecurityFilterChain", Filter.class))
.build();
mockMvc.perform(MockMvcRequestBuilders.get("/"))
.andExpect(MockMvcResultMatchers.header().string("Content-Security-Policy",
is("default-src 'self';")))
.andExpect(MockMvcResultMatchers.header().doesNotExist("Content-Security-Policy-Report-Only"));
}
@Test
public void contentSecurityPolicyReportOnlyConfiguration() throws Exception {
this.context = SpringApplication.run(VanillaWebConfiguration.class,
"--security.headers.content-security-policy=default-src 'self';",
"--security.headers.content-security-policy-mode=report-only");
MockMvc mockMvc = MockMvcBuilders
.webAppContextSetup((WebApplicationContext) this.context)
.addFilters((FilterChainProxy) this.context
.getBean("springSecurityFilterChain", Filter.class))
.build();
mockMvc.perform(MockMvcRequestBuilders.get("/"))
.andExpect(MockMvcResultMatchers.header().string("Content-Security-Policy-Report-Only",
is("default-src 'self';")))
.andExpect(MockMvcResultMatchers.header().doesNotExist("Content-Security-Policy"));
}
@Configuration
@Import(TestWebConfiguration.class)
@Order(Ordered.LOWEST_PRECEDENCE)

View File

@ -431,6 +431,8 @@ content into your application; rather pick only the properties that you need.
security.filter-order=0 # Security filter chain order.
security.filter-dispatcher-types=ASYNC, FORWARD, INCLUDE, REQUEST # Security filter chain dispatcher types.
security.headers.cache=true # Enable cache control HTTP headers.
security.headers.content-security-policy= # Value for content security policy header.
security.headers.content-security-policy-mode=default # Content security policy mode (default, report-only).
security.headers.content-type=true # Enable "X-Content-Type-Options" header.
security.headers.frame=true # Enable "X-Frame-Options" header.
security.headers.hsts= # HTTP Strict Transport Security (HSTS) mode (none, domain, all).