Add Cross Origin Policies headers

Add DSL support for Cross-Origin-Opener-Policy, Cross-Origin-Embedder-Policy and Cross-Origin-Resource-Policy headers

Closes gh-9385, gh-10118
This commit is contained in:
Marcus Da Coregio
2021-12-03 16:47:21 -03:00
committed by Eleftheria Stein-Kousathana
parent 7ec3b55ab3
commit 65426a40ec
38 changed files with 2513 additions and 9 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,11 +26,16 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
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 org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.header.writers.CrossOriginEmbedderPolicyHeaderWriter;
import org.springframework.security.web.header.writers.CrossOriginOpenerPolicyHeaderWriter;
import org.springframework.security.web.header.writers.CrossOriginResourcePolicyHeaderWriter;
import org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter.ReferrerPolicy;
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter.XFrameOptionsMode;
import org.springframework.test.web.servlet.MockMvc;
@@ -52,6 +57,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
* @author Eddú Meléndez
* @author Vedran Pavic
* @author Eleftheria Stein
* @author Marcus Da Coregio
*/
@ExtendWith(SpringTestContextExtension.class)
public class HeadersConfigurerTests {
@@ -514,6 +520,30 @@ public class HeadersConfigurerTests {
assertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.STRICT_TRANSPORT_SECURITY);
}
@Test
public void getWhenCustomCrossOriginPoliciesInLambdaThenCrossOriginPolicyHeadersWithCustomValuesInResponse()
throws Exception {
this.spring.register(CrossOriginCustomPoliciesInLambdaConfig.class).autowire();
MvcResult mvcResult = this.mvc.perform(get("/"))
.andExpect(header().string(HttpHeaders.CROSS_ORIGIN_OPENER_POLICY, "same-origin"))
.andExpect(header().string(HttpHeaders.CROSS_ORIGIN_EMBEDDER_POLICY, "require-corp"))
.andExpect(header().string(HttpHeaders.CROSS_ORIGIN_RESOURCE_POLICY, "same-origin")).andReturn();
assertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.CROSS_ORIGIN_OPENER_POLICY,
HttpHeaders.CROSS_ORIGIN_EMBEDDER_POLICY, HttpHeaders.CROSS_ORIGIN_RESOURCE_POLICY);
}
@Test
public void getWhenCustomCrossOriginPoliciesThenCrossOriginPolicyHeadersWithCustomValuesInResponse()
throws Exception {
this.spring.register(CrossOriginCustomPoliciesConfig.class).autowire();
MvcResult mvcResult = this.mvc.perform(get("/"))
.andExpect(header().string(HttpHeaders.CROSS_ORIGIN_OPENER_POLICY, "same-origin"))
.andExpect(header().string(HttpHeaders.CROSS_ORIGIN_EMBEDDER_POLICY, "require-corp"))
.andExpect(header().string(HttpHeaders.CROSS_ORIGIN_RESOURCE_POLICY, "same-origin")).andReturn();
assertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.CROSS_ORIGIN_OPENER_POLICY,
HttpHeaders.CROSS_ORIGIN_EMBEDDER_POLICY, HttpHeaders.CROSS_ORIGIN_RESOURCE_POLICY);
}
@EnableWebSecurity
static class HeadersConfig extends WebSecurityConfigurerAdapter {
@@ -1146,4 +1176,50 @@ public class HeadersConfigurerTests {
}
@EnableWebSecurity
static class CrossOriginCustomPoliciesInLambdaConfig {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
// @formatter:off
http.headers((headers) -> headers
.defaultsDisabled()
.crossOriginOpenerPolicy((policy) -> policy
.policy(CrossOriginOpenerPolicyHeaderWriter.CrossOriginOpenerPolicy.SAME_ORIGIN)
)
.crossOriginEmbedderPolicy((policy) -> policy
.policy(CrossOriginEmbedderPolicyHeaderWriter.CrossOriginEmbedderPolicy.REQUIRE_CORP)
)
.crossOriginResourcePolicy((policy) -> policy
.policy(CrossOriginResourcePolicyHeaderWriter.CrossOriginResourcePolicy.SAME_ORIGIN)
)
);
// @formatter:on
return http.build();
}
}
@EnableWebSecurity
static class CrossOriginCustomPoliciesConfig {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
// @formatter:off
http.headers()
.defaultsDisabled()
.crossOriginOpenerPolicy()
.policy(CrossOriginOpenerPolicyHeaderWriter.CrossOriginOpenerPolicy.SAME_ORIGIN)
.and()
.crossOriginEmbedderPolicy()
.policy(CrossOriginEmbedderPolicyHeaderWriter.CrossOriginEmbedderPolicy.REQUIRE_CORP)
.and()
.crossOriginResourcePolicy()
.policy(CrossOriginResourcePolicyHeaderWriter.CrossOriginResourcePolicy.SAME_ORIGIN);
// @formatter:on
return http.build();
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -48,6 +48,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
* @author Tim Ysewyn
* @author Josh Cummings
* @author Rafiullah Hamedy
* @author Marcus Da Coregio
*/
@ExtendWith(SpringTestContextExtension.class)
public class HttpHeadersConfigTests {
@@ -733,6 +734,53 @@ public class HttpHeadersConfigTests {
// @formatter:on
}
@Test
public void requestWhenCrossOriginOpenerPolicyWithSameOriginAllowPopupsThenRespondsWithSameOriginAllowPopups()
throws Exception {
this.spring.configLocations(this.xml("DefaultsDisabledWithCrossOriginOpenerPolicy")).autowire();
// @formatter:off
this.mvc.perform(get("/"))
.andExpect(status().isOk())
.andExpect(excludesDefaults())
.andExpect(header().string("Cross-Origin-Opener-Policy", "same-origin-allow-popups"));
// @formatter:on
}
@Test
public void requestWhenCrossOriginEmbedderPolicyWithRequireCorpThenRespondsWithRequireCorp() throws Exception {
this.spring.configLocations(this.xml("DefaultsDisabledWithCrossOriginEmbedderPolicy")).autowire();
// @formatter:off
this.mvc.perform(get("/"))
.andExpect(status().isOk())
.andExpect(excludesDefaults())
.andExpect(header().string("Cross-Origin-Embedder-Policy", "require-corp"));
// @formatter:on
}
@Test
public void requestWhenCrossOriginResourcePolicyWithSameOriginThenRespondsWithSameOrigin() throws Exception {
this.spring.configLocations(this.xml("DefaultsDisabledWithCrossOriginResourcePolicy")).autowire();
// @formatter:off
this.mvc.perform(get("/"))
.andExpect(status().isOk())
.andExpect(excludesDefaults())
.andExpect(header().string("Cross-Origin-Resource-Policy", "same-origin"));
// @formatter:on
}
@Test
public void requestWhenCrossOriginPoliciesRespondsCrossOriginPolicies() throws Exception {
this.spring.configLocations(this.xml("DefaultsDisabledWithCrossOriginPolicies")).autowire();
// @formatter:off
this.mvc.perform(get("/"))
.andExpect(status().isOk())
.andExpect(excludesDefaults())
.andExpect(header().string("Cross-Origin-Opener-Policy", "same-origin"))
.andExpect(header().string("Cross-Origin-Embedder-Policy", "require-corp"))
.andExpect(header().string("Cross-Origin-Resource-Policy", "same-origin"));
// @formatter:on
}
private static ResultMatcher includesDefaults() {
return includes(defaultHeaders);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,6 +30,9 @@ import org.springframework.http.HttpHeaders;
import org.springframework.security.test.web.reactive.server.WebTestClientBuilder;
import org.springframework.security.web.server.header.ContentSecurityPolicyServerHttpHeadersWriter;
import org.springframework.security.web.server.header.ContentTypeOptionsServerHttpHeadersWriter;
import org.springframework.security.web.server.header.CrossOriginEmbedderPolicyServerHttpHeadersWriter;
import org.springframework.security.web.server.header.CrossOriginOpenerPolicyServerHttpHeadersWriter;
import org.springframework.security.web.server.header.CrossOriginResourcePolicyServerHttpHeadersWriter;
import org.springframework.security.web.server.header.FeaturePolicyServerHttpHeadersWriter;
import org.springframework.security.web.server.header.ReferrerPolicyServerHttpHeadersWriter;
import org.springframework.security.web.server.header.ReferrerPolicyServerHttpHeadersWriter.ReferrerPolicy;
@@ -48,6 +51,7 @@ import static org.springframework.security.config.Customizer.withDefaults;
* @author Rob Winch
* @author Vedran Pavic
* @author Ankur Pathak
* @author Marcus Da Coregio
* @since 5.0
*/
public class HeaderSpecTests {
@@ -406,6 +410,53 @@ public class HeaderSpecTests {
assertHeaders();
}
@Test
public void headersWhenCrossOriginPoliciesCustomEnabledThenCustomCrossOriginPoliciesWritten() {
this.expectedHeaders.add(CrossOriginOpenerPolicyServerHttpHeadersWriter.OPENER_POLICY,
CrossOriginOpenerPolicyServerHttpHeadersWriter.CrossOriginOpenerPolicy.SAME_ORIGIN_ALLOW_POPUPS
.getPolicy());
this.expectedHeaders.add(CrossOriginEmbedderPolicyServerHttpHeadersWriter.EMBEDDER_POLICY,
CrossOriginEmbedderPolicyServerHttpHeadersWriter.CrossOriginEmbedderPolicy.REQUIRE_CORP.getPolicy());
this.expectedHeaders.add(CrossOriginResourcePolicyServerHttpHeadersWriter.RESOURCE_POLICY,
CrossOriginResourcePolicyServerHttpHeadersWriter.CrossOriginResourcePolicy.SAME_ORIGIN.getPolicy());
// @formatter:off
this.http.headers()
.crossOriginOpenerPolicy()
.policy(CrossOriginOpenerPolicyServerHttpHeadersWriter.CrossOriginOpenerPolicy.SAME_ORIGIN_ALLOW_POPUPS)
.and()
.crossOriginEmbedderPolicy()
.policy(CrossOriginEmbedderPolicyServerHttpHeadersWriter.CrossOriginEmbedderPolicy.REQUIRE_CORP)
.and()
.crossOriginResourcePolicy()
.policy(CrossOriginResourcePolicyServerHttpHeadersWriter.CrossOriginResourcePolicy.SAME_ORIGIN);
// @formatter:on
assertHeaders();
}
@Test
public void headersWhenCrossOriginPoliciesCustomEnabledInLambdaThenCustomCrossOriginPoliciesWritten() {
this.expectedHeaders.add(CrossOriginOpenerPolicyServerHttpHeadersWriter.OPENER_POLICY,
CrossOriginOpenerPolicyServerHttpHeadersWriter.CrossOriginOpenerPolicy.SAME_ORIGIN_ALLOW_POPUPS
.getPolicy());
this.expectedHeaders.add(CrossOriginEmbedderPolicyServerHttpHeadersWriter.EMBEDDER_POLICY,
CrossOriginEmbedderPolicyServerHttpHeadersWriter.CrossOriginEmbedderPolicy.REQUIRE_CORP.getPolicy());
this.expectedHeaders.add(CrossOriginResourcePolicyServerHttpHeadersWriter.RESOURCE_POLICY,
CrossOriginResourcePolicyServerHttpHeadersWriter.CrossOriginResourcePolicy.SAME_ORIGIN.getPolicy());
// @formatter:off
this.http.headers()
.crossOriginOpenerPolicy((policy) -> policy
.policy(CrossOriginOpenerPolicyServerHttpHeadersWriter.CrossOriginOpenerPolicy.SAME_ORIGIN_ALLOW_POPUPS)
)
.crossOriginEmbedderPolicy((policy) -> policy
.policy(CrossOriginEmbedderPolicyServerHttpHeadersWriter.CrossOriginEmbedderPolicy.REQUIRE_CORP)
)
.crossOriginResourcePolicy((policy) -> policy
.policy(CrossOriginResourcePolicyServerHttpHeadersWriter.CrossOriginResourcePolicy.SAME_ORIGIN)
);
// @formatter:on
assertHeaders();
}
private void expectHeaderNamesNotPresent(String... headerNames) {
for (String headerName : headerNames) {
this.expectedHeaders.remove(headerName);