From 86a521b0b0e062bc8d869780bb01adfd2c6c2e5d Mon Sep 17 00:00:00 2001 From: Joseph-Charlet <56923182+Joseph-Charlet@users.noreply.github.com> Date: Tue, 14 Feb 2023 11:03:41 +0100 Subject: [PATCH 1/9] Bug Server + CSPResource --- pw/pw-csp-nonce/server/pom.xml | 5 + .../bookstore/web/rest/CSPResource.java | 104 +++++++++++++++++- 2 files changed, 107 insertions(+), 2 deletions(-) diff --git a/pw/pw-csp-nonce/server/pom.xml b/pw/pw-csp-nonce/server/pom.xml index cf2a3b68..6d3fd45e 100644 --- a/pw/pw-csp-nonce/server/pom.xml +++ b/pw/pw-csp-nonce/server/pom.xml @@ -91,6 +91,11 @@ + + io.dropwizard.metrics + metrics-annotation + 4.2.15 + javax.xml.bind jaxb-api diff --git a/pw/pw-csp-nonce/server/src/main/java/com/worldline/bookstore/web/rest/CSPResource.java b/pw/pw-csp-nonce/server/src/main/java/com/worldline/bookstore/web/rest/CSPResource.java index 44c10b2d..c4c3991a 100644 --- a/pw/pw-csp-nonce/server/src/main/java/com/worldline/bookstore/web/rest/CSPResource.java +++ b/pw/pw-csp-nonce/server/src/main/java/com/worldline/bookstore/web/rest/CSPResource.java @@ -16,14 +16,114 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import com.codahale.metrics.annotation.Timed; +//import com.codahale.metrics.annotation.Timed; /** - * REST controller for managing Content-Security-Policy confuguration with random nonce. + * REST controller for managing Content-Security-Policy confuguration with + * random nonce. */ @RestController @RequestMapping("/api") public class CSPResource { + public CSPwrapper test = CSPwrapper(); + private final Logger log = LoggerFactory.getLogger(CSPResource.class); + + /** Used for Script Nonce */ + private SecureRandom prng = null; + + @GetMapping("/csp") + // Add Script Nonce CSP Policy + public ResponseEntity generateCSP(HttpServletResponse response) { + // --Get its digest + MessageDigest sha; + // --Generate a random number + String randomNum; + try { + this.prng = SecureRandom.getInstance("SHA1PRNG"); + randomNum = new Integer(this.prng.nextInt()).toString(); + sha = MessageDigest.getInstance("SHA-1"); + } catch (NoSuchAlgorithmException e) { + return new ResponseEntity<>(Collections.singletonMap("CSPException", e.getLocalizedMessage()), + HttpStatus.INTERNAL_SERVER_ERROR); + } + + byte[] digest = sha.digest(randomNum.getBytes()); + + // --Encode it into HEXA + char[] scriptNonce = Hex.encode(digest); + + String csp = "script-src" + + " 'unsafe-eval' 'strict-dynamic' " + + " 'nonce-" + String.valueOf(scriptNonce) + "'" + + " 'sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4='" + + // SRI hashes for + // https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js (work only + // for Chrome) + ";" + + // add connect-src directive to adapt CSP over cross-origin requests (CORS) + "connect-src" + + " http://localhost:8080 http://localhost:4200 ws://localhost:4200" + + ";" + + " style-src" + + " 'self' 'unsafe-inline'" + + ";" + + " font-src" + + " 'self' " + + ";" + + " img-src" + + " 'self' data:" + + ";" + + " child-src" + + " 'self' " + + ";" + + " object-src" + + " 'none' " + + ";" + + " default-src" + + " 'self' "; + + CSP conf = new CSP(csp); + conf.setNonce(String.valueOf(scriptNonce)); + + log.debug(conf.toString()); + + return ResponseEntity.ok(conf); + } } + +/* + * package com.worldline.bookstore.web.rest; + * + * import java.security.MessageDigest; + * import java.security.NoSuchAlgorithmException; + * import java.security.SecureRandom; + * import java.util.Collections; + * + * import javax.servlet.http.HttpServletResponse; + * + * import org.slf4j.Logger; + * import org.slf4j.LoggerFactory; + * import org.springframework.http.HttpStatus; + * import org.springframework.http.ResponseEntity; + * import org.springframework.security.crypto.codec.Hex; + * import org.springframework.web.bind.annotation.GetMapping; + * import org.springframework.web.bind.annotation.RequestMapping; + * import org.springframework.web.bind.annotation.RestController; + */ + +// import com.codahale.metrics.annotation.Timed; + +/** + * REST controller for managing Content-Security-Policy confuguration with + * random nonce. + */ +/* + * @RestController + * + * @RequestMapping("/api") + * public class CSPResource { + * + * } + */ \ No newline at end of file From 776e12685af3f1264bda1acb03aa290baaa20263 Mon Sep 17 00:00:00 2001 From: Joseph-Charlet <56923182+Joseph-Charlet@users.noreply.github.com> Date: Tue, 14 Feb 2023 11:14:24 +0100 Subject: [PATCH 2/9] SecurityConfiguration.java --- .../config/SecurityConfiguration.java | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/pw/pw-csp-nonce/server/src/main/java/com/worldline/bookstore/config/SecurityConfiguration.java b/pw/pw-csp-nonce/server/src/main/java/com/worldline/bookstore/config/SecurityConfiguration.java index 4ce8239d..51d6f467 100644 --- a/pw/pw-csp-nonce/server/src/main/java/com/worldline/bookstore/config/SecurityConfiguration.java +++ b/pw/pw-csp-nonce/server/src/main/java/com/worldline/bookstore/config/SecurityConfiguration.java @@ -105,8 +105,52 @@ protected void configure(HttpSecurity http) throws Exception { ; // TODO uncomment this line to activate JWT filter + // setCspConfig(http); + } + // Add CSP hash for the following inline scripting (see + // https://report-uri.io/home/hash) : + // - "document.write('

Inline scripting is not recommended! But if you + // have not the choice, secure your app with CSP

');" ==> + // 'sha256-lK+Y3vDnNUrD/ZPLGsnM6B+euoBxZ/MyiIbY2G5VoPw=' + // - inline style ... + + /* + * private void setCspConfig(HttpSecurity http) throws Exception { + * http + * .headers() + * .contentSecurityPolicy( + * "script-src" + + * " 'none' " + + * // "'unsafe-eval' 'unsafe-inline' " + + * ";" + + * // add connect-src directive to adapt CSP over cross-origin requests (CORS) + * "connect-src" + + * " 'self'" + + * ";" + + * " style-src" + + * " 'self' 'unsafe-inline'" + + * ";" + + * " font-src" + + * " 'self' " + + * ";" + + * " img-src" + + * " 'self' " + + * ";" + + * " child-src" + + * " 'self' " + + * ";" + + * " object-src" + + * " 'none' " + + * ";" + + * " report-uri" + + * " 'http://localhost:4200' " + + * ";" + + * " default-src" + + * " 'self' ");// .reportOnly(); + * } + */ private JWTConfigurer securityConfigurerAdapter() { return new JWTConfigurer(tokenProvider); } From 0c660e52f3758e93943a36dc4edab5255024cdc7 Mon Sep 17 00:00:00 2001 From: Joseph-Charlet <56923182+Joseph-Charlet@users.noreply.github.com> Date: Tue, 14 Feb 2023 11:16:08 +0100 Subject: [PATCH 3/9] cspConfigService.ts --- .../src/app/services/cspConfigService.ts | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/pw/pw-csp-nonce/client/src/app/services/cspConfigService.ts b/pw/pw-csp-nonce/client/src/app/services/cspConfigService.ts index ce133679..936c81bf 100644 --- a/pw/pw-csp-nonce/client/src/app/services/cspConfigService.ts +++ b/pw/pw-csp-nonce/client/src/app/services/cspConfigService.ts @@ -1,9 +1,41 @@ import { Injectable, Injector } from '@angular/core'; import { HttpClient, HttpResponse } from '@angular/common/http'; +import { CSP } from '../beans/csp'; @Injectable() // This service gets the Content-Security-Policy and a random nonce from a REST api endpoint /api/csp export class CspConfig { + private _config: any; + private _nonce: any; + private http: HttpClient; + private csp: CSP; + // can't use classical Angular DI for HttpClient here, because of "cyclic dependency" issues + // Use Injector service to instanciate HttpClient + constructor(injector: Injector) { + this.http = injector.get(HttpClient); + this.csp = new CSP("", ""); + } -} + // Load Content-Security-Policy from a REST api endpoint + // The returned data will contain the CSP configuration ('value') and the a random generated nonce ('nonce') + load(): Promise { + return this.http.get('/api/csp') + .toPromise() + .then(data => { + + //this.csp = data; + //this._config = data!['value']; + //this._nonce = data['nonce']; + return data; + }) + } + + get config(): any { + return this._config; + } + + get nonce(): any { + return this._nonce; + } +} \ No newline at end of file From 9d9ff5acd1c1532f21e3c5b2695ede00a5ab8b3d Mon Sep 17 00:00:00 2001 From: Joseph-Charlet <56923182+Joseph-Charlet@users.noreply.github.com> Date: Tue, 14 Feb 2023 11:17:36 +0100 Subject: [PATCH 4/9] cspConfigService.ts V2 --- pw/pw-csp-nonce/client/src/app/services/cspConfigService.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pw/pw-csp-nonce/client/src/app/services/cspConfigService.ts b/pw/pw-csp-nonce/client/src/app/services/cspConfigService.ts index 936c81bf..ea4b4f80 100644 --- a/pw/pw-csp-nonce/client/src/app/services/cspConfigService.ts +++ b/pw/pw-csp-nonce/client/src/app/services/cspConfigService.ts @@ -24,9 +24,8 @@ export class CspConfig { .toPromise() .then(data => { - //this.csp = data; - //this._config = data!['value']; - //this._nonce = data['nonce']; + this._config = JSON.parse(JSON.stringify(data))['value']; + this._nonce = JSON.parse(JSON.stringify(data))['nonce']; return data; }) } From 8b98f09a918be70185368efdf37086183f9a963c Mon Sep 17 00:00:00 2001 From: Sheron17 Date: Tue, 14 Feb 2023 11:59:28 +0100 Subject: [PATCH 5/9] CSP.java updated ! --- .../src/main/java/com/worldline/bookstore/web/rest/CSP.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pw/pw-csp-nonce/server/src/main/java/com/worldline/bookstore/web/rest/CSP.java b/pw/pw-csp-nonce/server/src/main/java/com/worldline/bookstore/web/rest/CSP.java index 8b8474eb..6eab5ba9 100644 --- a/pw/pw-csp-nonce/server/src/main/java/com/worldline/bookstore/web/rest/CSP.java +++ b/pw/pw-csp-nonce/server/src/main/java/com/worldline/bookstore/web/rest/CSP.java @@ -2,7 +2,7 @@ public class CSP { - /*private String value; + public String value; private String nonce; public String getNonce() { @@ -29,5 +29,5 @@ public void setValue(String value) { @Override public String toString() { return "CSP [value=" + value + ", nonce=" + nonce + "]"; - }*/ + } } From a26923490ecfe8dc8e86ad3d2bc0d4e87d4ad029 Mon Sep 17 00:00:00 2001 From: Sheron17 Date: Tue, 14 Feb 2023 12:02:15 +0100 Subject: [PATCH 6/9] app.component.ts updated ! --- .../client/src/app/app.component.ts | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/pw/pw-csp-nonce/client/src/app/app.component.ts b/pw/pw-csp-nonce/client/src/app/app.component.ts index 8f99046d..d7f6a92a 100644 --- a/pw/pw-csp-nonce/client/src/app/app.component.ts +++ b/pw/pw-csp-nonce/client/src/app/app.component.ts @@ -1,6 +1,7 @@ import { Component } from '@angular/core'; import { Router } from '@angular/router'; import { UserService } from './services/userService'; +import { CspConfig } from './services/cspConfigService'; @Component({ selector: 'app-root', @@ -8,6 +9,63 @@ import { UserService } from './services/userService'; templateUrl: './app.component.html', providers: [], }) + +export class AppComponent { + private csp: string = ""; + private nonce: string = ""; + private modifiedNonce: string = ""; + + constructor( + private router: Router, + public userService: UserService, + public cspConfig: CspConfig) { + + cspConfig.load().then( + data => { + + this.csp = data['value']; + //this.nonce = data['nonce']; + this.nonce = "z"; + + console.debug('csp : ' + this.csp); + console.debug('nonce : ' + this.nonce); + console.debug('modified nonce : ' + this.modifiedNonce); + + // can't use the Meta#addTags() method to set CSP because it will insert the meta tag too late, so we add it "manually" + var meta = ""; + this.renderHtml(meta, 'head'); + console.log('content-security-policy meta : ' + meta); + + // Add secure inline scripting (a script block with a nonce) + // The script will just render a message at the bottom of the page + // (here, we don't use document.write method otherwise it will replace the whole page rendering) + var yourHtmlString = // nonce='" + this.nonce + "' + ""; + this.renderHtml(yourHtmlString, 'head'); + console.log('inline scripting !!! ', yourHtmlString); + }); + } + + /** + * + * Renders an html portion inside a given html tag + * @param message: a string which represents the html portion to render in the page + * @param parentTag : the html tag name in which the html portion will be inserted as a first child + */ + private renderHtml(message: string, parentTag: string) { + var fragment = document.createRange().createContextualFragment(message); + document.getElementsByTagName(parentTag)[0].appendChild(fragment); + } + + logout() { + this.userService.logout(); + } +} + +/* export class AppComponent { constructor(private router: Router, public userService: UserService) {} @@ -15,3 +73,4 @@ export class AppComponent { this.userService.logout(); } } +*/ \ No newline at end of file From 8eb6f2f976158466efa2f5d580ee97599b79ec50 Mon Sep 17 00:00:00 2001 From: Sheron17 Date: Tue, 14 Feb 2023 12:03:54 +0100 Subject: [PATCH 7/9] app.modules.ts updated ! --- pw/pw-csp-nonce/client/src/app/app.module.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pw/pw-csp-nonce/client/src/app/app.module.ts b/pw/pw-csp-nonce/client/src/app/app.module.ts index 78f979d7..aeb4cd4c 100644 --- a/pw/pw-csp-nonce/client/src/app/app.module.ts +++ b/pw/pw-csp-nonce/client/src/app/app.module.ts @@ -37,7 +37,7 @@ import { Basket } from './basket/basket'; import { Profile } from './profile/profile'; import { Login } from './login/login'; -// import {CspConfig} from './services/cspConfigService'; +import { CspConfig } from './services/cspConfigService'; @NgModule({ imports: [ @@ -49,6 +49,7 @@ import { Login } from './login/login'; HttpClientModule, ], providers: [ + CspConfig, UserService, BooksService, DataContainerService, @@ -86,4 +87,4 @@ import { Login } from './login/login'; ], bootstrap: [AppComponent], }) -export class AppModule {} +export class AppModule { } From e1532158d702937b1f5739a93665b731e92c8ed9 Mon Sep 17 00:00:00 2001 From: Sheron17 Date: Wed, 15 Feb 2023 09:29:08 +0100 Subject: [PATCH 8/9] old csp class import deleted --- pw/pw-csp-nonce/client/src/app/services/cspConfigService.ts | 3 --- .../java/com/worldline/bookstore/web/rest/CSPResource.java | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/pw/pw-csp-nonce/client/src/app/services/cspConfigService.ts b/pw/pw-csp-nonce/client/src/app/services/cspConfigService.ts index ea4b4f80..2534a4b6 100644 --- a/pw/pw-csp-nonce/client/src/app/services/cspConfigService.ts +++ b/pw/pw-csp-nonce/client/src/app/services/cspConfigService.ts @@ -1,6 +1,5 @@ import { Injectable, Injector } from '@angular/core'; import { HttpClient, HttpResponse } from '@angular/common/http'; -import { CSP } from '../beans/csp'; @Injectable() // This service gets the Content-Security-Policy and a random nonce from a REST api endpoint /api/csp @@ -8,13 +7,11 @@ export class CspConfig { private _config: any; private _nonce: any; private http: HttpClient; - private csp: CSP; // can't use classical Angular DI for HttpClient here, because of "cyclic dependency" issues // Use Injector service to instanciate HttpClient constructor(injector: Injector) { this.http = injector.get(HttpClient); - this.csp = new CSP("", ""); } // Load Content-Security-Policy from a REST api endpoint diff --git a/pw/pw-csp-nonce/server/src/main/java/com/worldline/bookstore/web/rest/CSPResource.java b/pw/pw-csp-nonce/server/src/main/java/com/worldline/bookstore/web/rest/CSPResource.java index c4c3991a..c99827ce 100644 --- a/pw/pw-csp-nonce/server/src/main/java/com/worldline/bookstore/web/rest/CSPResource.java +++ b/pw/pw-csp-nonce/server/src/main/java/com/worldline/bookstore/web/rest/CSPResource.java @@ -26,7 +26,7 @@ @RequestMapping("/api") public class CSPResource { - public CSPwrapper test = CSPwrapper(); + // public CSPwrapper test = CSPwrapper(); private final Logger log = LoggerFactory.getLogger(CSPResource.class); /** Used for Script Nonce */ From eefbf8a34430ed36bb10834ff1f77654846aa1be Mon Sep 17 00:00:00 2001 From: Sheron17 Date: Wed, 15 Feb 2023 11:22:28 +0100 Subject: [PATCH 9/9] inline scripting nonce added --- pw/pw-csp-nonce/client/src/app/app.component.ts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/pw/pw-csp-nonce/client/src/app/app.component.ts b/pw/pw-csp-nonce/client/src/app/app.component.ts index d7f6a92a..ae194067 100644 --- a/pw/pw-csp-nonce/client/src/app/app.component.ts +++ b/pw/pw-csp-nonce/client/src/app/app.component.ts @@ -13,7 +13,6 @@ import { CspConfig } from './services/cspConfigService'; export class AppComponent { private csp: string = ""; private nonce: string = ""; - private modifiedNonce: string = ""; constructor( private router: Router, @@ -22,14 +21,11 @@ export class AppComponent { cspConfig.load().then( data => { - this.csp = data['value']; - //this.nonce = data['nonce']; - this.nonce = "z"; + this.nonce = data['nonce']; console.debug('csp : ' + this.csp); console.debug('nonce : ' + this.nonce); - console.debug('modified nonce : ' + this.modifiedNonce); // can't use the Meta#addTags() method to set CSP because it will insert the meta tag too late, so we add it "manually" var meta = ""; @@ -39,8 +35,8 @@ export class AppComponent { // Add secure inline scripting (a script block with a nonce) // The script will just render a message at the bottom of the page // (here, we don't use document.write method otherwise it will replace the whole page rendering) - var yourHtmlString = // nonce='" + this.nonce + "' - "";