From f16ed0b658a13df7213f449b9e87f052858a03d2 Mon Sep 17 00:00:00 2001 From: Bhaskara Date: Sun, 26 Dec 2021 08:14:50 +0530 Subject: [PATCH] =?UTF-8?q?BAEL-4837=20-=20Content=20Security=20Policy=20u?= =?UTF-8?q?sing=20Spring=20Security=20and=20Spring=20=E2=80=A6=20(#11603)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * BAEL-4837 - Content Security Policy using Spring Security and Spring Boot * Application Code * Formatted the code * Reformatted the test cases as per review comments * Removed the formatters and deleted extra spaces Co-authored-by: Bhaskara Navuluri --- .../spring-security-web-boot-3/pom.xml | 23 ++++++- .../ContentSecurityPolicyApplication.java | 11 +++ .../ContentSecurityPolicyController.java | 23 +++++++ ...ntSecurityPolicySecurityConfiguration.java | 26 +++++++ .../src/main/resources/static/index.html | 67 +++++++++++++++++++ .../src/main/resources/static/main.css | 26 +++++++ .../ContentSecurityPolicyUnitTest.java | 64 ++++++++++++++++++ 7 files changed, 237 insertions(+), 3 deletions(-) create mode 100644 spring-security-modules/spring-security-web-boot-3/src/main/java/com/baeldung/contentsecuritypolicy/ContentSecurityPolicyApplication.java create mode 100644 spring-security-modules/spring-security-web-boot-3/src/main/java/com/baeldung/contentsecuritypolicy/ContentSecurityPolicyController.java create mode 100644 spring-security-modules/spring-security-web-boot-3/src/main/java/com/baeldung/contentsecuritypolicy/ContentSecurityPolicySecurityConfiguration.java create mode 100644 spring-security-modules/spring-security-web-boot-3/src/main/resources/static/index.html create mode 100644 spring-security-modules/spring-security-web-boot-3/src/main/resources/static/main.css create mode 100644 spring-security-modules/spring-security-web-boot-3/src/test/java/com/baeldung/contentsecuritypolicy/ContentSecurityPolicyUnitTest.java diff --git a/spring-security-modules/spring-security-web-boot-3/pom.xml b/spring-security-modules/spring-security-web-boot-3/pom.xml index b26ca094be..5bfd07c8b5 100644 --- a/spring-security-modules/spring-security-web-boot-3/pom.xml +++ b/spring-security-modules/spring-security-web-boot-3/pom.xml @@ -1,7 +1,5 @@ - + 4.0.0 spring-security-web-boot-3 0.0.1-SNAPSHOT @@ -25,6 +23,25 @@ org.springframework.boot spring-boot-starter-web + + commons-io + commons-io + 2.11.0 + + + org.webjars + webjars-locator-core + + + org.webjars + bootstrap + 5.1.1 + + + org.webjars + jquery + 3.6.0 + org.springframework.boot spring-boot-starter-test diff --git a/spring-security-modules/spring-security-web-boot-3/src/main/java/com/baeldung/contentsecuritypolicy/ContentSecurityPolicyApplication.java b/spring-security-modules/spring-security-web-boot-3/src/main/java/com/baeldung/contentsecuritypolicy/ContentSecurityPolicyApplication.java new file mode 100644 index 0000000000..d009f64918 --- /dev/null +++ b/spring-security-modules/spring-security-web-boot-3/src/main/java/com/baeldung/contentsecuritypolicy/ContentSecurityPolicyApplication.java @@ -0,0 +1,11 @@ +package com.baeldung.contentsecuritypolicy; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class ContentSecurityPolicyApplication { + public static void main(String[] args) { + SpringApplication.run(ContentSecurityPolicyApplication.class, args); + } +} \ No newline at end of file diff --git a/spring-security-modules/spring-security-web-boot-3/src/main/java/com/baeldung/contentsecuritypolicy/ContentSecurityPolicyController.java b/spring-security-modules/spring-security-web-boot-3/src/main/java/com/baeldung/contentsecuritypolicy/ContentSecurityPolicyController.java new file mode 100644 index 0000000000..f57833fd7b --- /dev/null +++ b/spring-security-modules/spring-security-web-boot-3/src/main/java/com/baeldung/contentsecuritypolicy/ContentSecurityPolicyController.java @@ -0,0 +1,23 @@ +package com.baeldung.contentsecuritypolicy; + +import org.apache.commons.io.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +@RestController +public class ContentSecurityPolicyController { + private static final Logger logger = LoggerFactory.getLogger(ContentSecurityPolicyController.class); + + @PostMapping("/report") + public void report(HttpServletRequest request) throws IOException { + if (logger.isInfoEnabled()) { + logger.info("Report: {}", IOUtils.toString(request.getInputStream(), StandardCharsets.UTF_8)); + } + } +} diff --git a/spring-security-modules/spring-security-web-boot-3/src/main/java/com/baeldung/contentsecuritypolicy/ContentSecurityPolicySecurityConfiguration.java b/spring-security-modules/spring-security-web-boot-3/src/main/java/com/baeldung/contentsecuritypolicy/ContentSecurityPolicySecurityConfiguration.java new file mode 100644 index 0000000000..1593af9c66 --- /dev/null +++ b/spring-security-modules/spring-security-web-boot-3/src/main/java/com/baeldung/contentsecuritypolicy/ContentSecurityPolicySecurityConfiguration.java @@ -0,0 +1,26 @@ +package com.baeldung.contentsecuritypolicy; + +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.web.header.writers.StaticHeadersWriter; + +@Configuration +public class ContentSecurityPolicySecurityConfiguration extends WebSecurityConfigurerAdapter { + private static final String REPORT_TO = "{\"group\":\"csp-violation-report\",\"max_age\":2592000,\"endpoints\":[{\"url\":\"https://localhost:8080/report\"}]}"; + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.csrf() + .disable() + .authorizeRequests() + .antMatchers("/**") + .permitAll() + .and() + .headers() + .addHeaderWriter(new StaticHeadersWriter("Report-To", REPORT_TO)) + .xssProtection() + .and() + .contentSecurityPolicy("form-action 'self'; report-uri /report; report-to csp-violation-report"); + } +} diff --git a/spring-security-modules/spring-security-web-boot-3/src/main/resources/static/index.html b/spring-security-modules/spring-security-web-boot-3/src/main/resources/static/index.html new file mode 100644 index 0000000000..52ecb198df --- /dev/null +++ b/spring-security-modules/spring-security-web-boot-3/src/main/resources/static/index.html @@ -0,0 +1,67 @@ + + + + Content Security Policy + + + + + + + + + + +
+
+ +
+
+ +
+
+
+
+
+
+

Login

+
+
+
+ + +
+
+
+ + +
+ +
+ +
+
+
+ +
+
+ +
+ + + \ No newline at end of file diff --git a/spring-security-modules/spring-security-web-boot-3/src/main/resources/static/main.css b/spring-security-modules/spring-security-web-boot-3/src/main/resources/static/main.css new file mode 100644 index 0000000000..dac839bc64 --- /dev/null +++ b/spring-security-modules/spring-security-web-boot-3/src/main/resources/static/main.css @@ -0,0 +1,26 @@ +html, body{ + font-family: Raleway, serif; + background-color: #f5f5f5; +} +.navbar +{ + background-color: #63b175 !important; +} +.navbar-brand +{ + font-size: 1.75rem; + font-weight: bold; +} +/* +hr{ + border: 0px dotted rgba(249, 249, 249, 0.88); +}*/ + +hr { + border: 0; + border-bottom: 1px dashed #969595; + background: #969595; +} +label{ + font-weight: bold; +} \ No newline at end of file diff --git a/spring-security-modules/spring-security-web-boot-3/src/test/java/com/baeldung/contentsecuritypolicy/ContentSecurityPolicyUnitTest.java b/spring-security-modules/spring-security-web-boot-3/src/test/java/com/baeldung/contentsecuritypolicy/ContentSecurityPolicyUnitTest.java new file mode 100644 index 0000000000..0e06a7ef35 --- /dev/null +++ b/spring-security-modules/spring-security-web-boot-3/src/test/java/com/baeldung/contentsecuritypolicy/ContentSecurityPolicyUnitTest.java @@ -0,0 +1,64 @@ +package com.baeldung.contentsecuritypolicy; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; + +import org.springframework.http.HttpStatus; + +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; + +import javax.servlet.http.HttpServletResponse; + +import java.util.Collection; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; + +@WebMvcTest +@AutoConfigureMockMvc +@DisplayName("Content Security Policy Unit Tests") +class ContentSecurityPolicyUnitTest { + + @Autowired + private MockMvc mockMvc; + + @Test + @DisplayName("Test to Check Bad URL") + void whenWrongUri_thenThrow404() throws Exception { + MvcResult result = mockMvc.perform(post("/reports").content("").contentType(MediaType.APPLICATION_JSON)).andReturn(); + + assertEquals(HttpStatus.NOT_FOUND.value(), result.getResponse().getStatus()); + } + + @Test + @DisplayName("Test to Check Page rendering") + void whenGet_thenRenderPage() throws Exception { + MvcResult result = mockMvc.perform(get("/").content("")).andReturn(); + + assertEquals(HttpStatus.OK.value(), result.getResponse().getStatus()); + assertEquals("text/html", MediaType.TEXT_HTML_VALUE); + } + + @Test + @DisplayName("Test to Check CSP headers") + void whenGet_thenCheckCspHeaders() throws Exception { + MvcResult result = mockMvc.perform(get("/").content("")).andReturn(); + HttpServletResponse response = result.getResponse(); + Collection headers = response.getHeaderNames(); + + assertNotNull(result); + assertNotNull(headers); + assertEquals(HttpStatus.OK.value(), response.getStatus()); + assertEquals("text/html", MediaType.TEXT_HTML_VALUE); + assertTrue(headers.contains("Report-To")); + assertTrue(headers.contains("Content-Security-Policy")); + } +}