CSP and headers improvements, js nonce

This commit is contained in:
Matteo Gheza 2021-05-16 21:32:43 +02:00
parent ebbbe52753
commit 6ddb1f8c6a
13 changed files with 89 additions and 60 deletions

View File

@ -34,11 +34,21 @@ class tools
public $db;
public $profiler_enabled;
public $profiler_last_name = "";
public $script_nonce = null;
public function generateNonce($bytes_lenght = 16, $base64_encode = false){
$nonce = bin2hex(random_bytes($bytes_lenght));
if($base64_encode){
$nonce = base64_encode($nonce);
}
return $nonce;
}
public function __construct($db, $profiler_enabled)
{
$this->db = $db;
$this->profiler_enabled = $profiler_enabled;
$this->script_nonce = $this->generateNonce(16);
}
public function validate_form($data, $expected_value=null, $data_source=null)
@ -910,19 +920,28 @@ function init_class($enableDebugger=true, $headers=true)
if($headers) {
//TODO adding require-trusted-types-for 'script';
$csp = "default-src 'self' data: *.tile.openstreetmap.org nominatim.openstreetmap.org; connect-src 'self' *.sentry.io nominatim.openstreetmap.org; script-src 'self' 'unsafe-inline' 'unsafe-eval'; img-src 'self' data: *.tile.openstreetmap.org; object-src; style-src 'self' 'unsafe-inline';";
$csp_rules = [
"default-src 'self' data: *.tile.openstreetmap.org nominatim.openstreetmap.org",
"connect-src 'self' *.sentry.io nominatim.openstreetmap.org",
"script-src 'nonce-{$tools->script_nonce}' 'self'",
"img-src 'self' data: *.tile.openstreetmap.org",
"object-src",
"style-src 'self' 'unsafe-inline'",
"base-uri 'self'"
];
if(defined(SENTRY_CSP_REPORT_URI) && SENTRY_CSP_REPORT_URI !== false){
$csp .= " report-uri ".SENTRY_CSP_REPORT_URI.";";
$csp_rules[] = "report-uri ".SENTRY_CSP_REPORT_URI;
}
$csp = implode("; ", $csp_rules);
if(!isset($_COOKIE["JSless"]) && (isset($_GET["JSless"]) ? !$_GET["JSless"] : true)){
header("Content-Security-Policy: $csp");
header("X-XSS-Protection: 1; mode=block");
header("X-Content-Type-Options: nosniff");
header("Permissions-Policy: interest-cohort=(), camera=(), microphone=(), payment=(), usb=()");
header("Referrer-Policy: no-referrer");
header("X-Frame-Options: DENY");
}
header("Content-Security-Policy: $csp");
header("X-Content-Security-Policy: $csp");
header("X-WebKit-CSP: $csp");
header("X-XSS-Protection: 1; mode=block");
header("X-Content-Type-Options: nosniff");
header("Feature-Policy: autoplay 'none'; camera 'none'; microphone 'none'; payment 'none'");
}
//var_dump($user);
//exit();
if(SENTRY_ENABLED){
Sentry\configureScope(function (Sentry\State\Scope $scope): void {

View File

@ -161,7 +161,7 @@ EOT);
echo("</div>");
?>
<script>
<script nonce="<?php echo(isset($_GET["nonce"]) ? $_GET["nonce"] : ""); ?>">
function init_modal() {
<?php if($orienation == "landscape"){ ?>$(".modal-dialog").css("max-width", "99%");<?php } ?>
var isMouseDown = false;

View File

@ -10,7 +10,7 @@
<meta name="robots" content="none">
<link rel="manifest" href="manifest.webmanifest">
{% if delete_caches %}
<script>
<script nonce="{{ nonce }}">
//from https://stackoverflow.com/a/57149239
if ('caches' in window) {
console.log("Deleting caches...");
@ -28,7 +28,7 @@
</script>
{% endif %}
{% if delete_service_workers %}
<script>
<script nonce="{{ nonce }}">
//from https://stackoverflow.com/a/47515250
if(window.navigator && navigator.serviceWorker) {
console.log("UnRegistering service workers...");
@ -48,7 +48,7 @@
</script>
{% endif %}
{{ script('main.js') }}
<script>$.fn.loading.defaults.message = "{{ 'Loading...'|t }}";</script>
<script nonce="{{ nonce }}">$.fn.loading.defaults.message = "{{ 'Loading...'|t }}";</script>
{% endblock %}
{% if enable_debug_bar %}{{ debug_bar_head|raw }}{% endif %}
</head>

View File

@ -40,7 +40,7 @@
{% endif %}
{% endfor %}
</div>
<script>
<script nonce="{{ nonce }}">
$('.chief').on('change', function () {
$('input[name="' + this.name + '"]').not(this).prop('checked', false);
});
@ -126,7 +126,7 @@
<button id="submit_button" type="submit" class="btn btn-primary">{{ 'Submit'|t }}</button>
</div>
</form>
<script>
<script nonce="{{ nonce }}">
$( "#types" ).change(function() {
$('#empty_option').remove();
var type = "";
@ -197,7 +197,7 @@
<input id="id" type="hidden" value="{{ service.id }}" name="id"></input>
<button id="remove" type="submit">{{ 'Submit'|t }}</button>
</form>
<script>
<script nonce="{{ nonce }}">
$('form').submit(function () {
return confirm("{{ 'The action cannot be canceled. Are you sure you want to continue?'|t }}");
});

View File

@ -40,7 +40,7 @@
{% endif %}
{% endfor %}
</div>
<script>
<script nonce="{{ nonce }}">
$('.chief').on('change', function () {
$('input[name="' + this.name + '"]').not(this).prop('checked', false);
});
@ -96,7 +96,7 @@
<button id="submit_button" type="submit" class="btn btn-primary">{{ 'Submit'|t }}</button>
</div>
</form>
<script>
<script nonce="{{ nonce }}">
{% if training.modalità == "edit" %}
{% if option('use_location_picker') %}
{% set place = values.place|split('#')[0] %}
@ -121,7 +121,7 @@
<input id="id" type="hidden" value="{{ training.id }}" name="id"></input>
<button id="remove" type="submit">{{ 'Submit'|t }}</button>
</form>
<script>
<script nonce="{{ nonce }}">
$('form').submit(function () {
return confirm("{{ 'The action cannot be canceled. Are you sure you want to continue?'|t }}");
});

View File

@ -69,7 +69,7 @@
<input id="id" type="hidden" value="{{ id }}" name="id"></input>
<button id="remove" type="submit">{{ 'Submit'|t }}</button>
</form>
<script>
<script nonce="{{ nonce }}">
$('form').submit(function () {
return confirm("{{ 'The action cannot be canceled. Are you sure you want to continue?'|t }}");
});

View File

@ -4,6 +4,32 @@
{% endblock %}
{% block content %}
{% if error %}
<div id="err" class="alert alert-danger m-3" role="alert">
<h4 class="alert-heading">{{ error.text|t }}</h4>
<p>{{ 'Error'|t}} {{ 'code'|t}} {{ error.code }}. {{ 'Check the entered credentials and make sure they are correct'|t }}.</p>
<img src='{{ urlsoftware }}/resources/images/{{ error_image }}'></img>
</div>
<script nonce="{{ nonce }}">
const noSaveData = 'connection' in navigator === false || ('connection' in navigator && !navigator.connection.saveData);
if (noSaveData) {
let playerScript = document.createElement("script");
playerScript.setAttribute("src", "{{ urlsoftware }}/resources/dist/{{ resource('players.js') }}");
playerScript.setAttribute("nonce", "{{ nonce }}");
playerScript.setAttribute("async", "false");
playerScript.onload = function() {
console.log("player script loaded");
var sound = new Howl({
src: ['{{ urlsoftware }}/resources/sounds/{{ error_sound }}'],
autoplay: true
});
sound.play();
};
document.head.insertBefore(playerScript, document.head.firstElementChild);
}
$("#err").delay(20000).fadeOut(300);
</script>
{% endif %}
<div class="align-items-center container d-flex justify-content-center" id="modulogin">
<form method="post">
<img alt="VVF" src="{{ urlsoftware }}/resources/images/logo.png" class="img-resposive"><br><br>
@ -25,31 +51,6 @@
{% endif %}
<input type="submit" name="login" class="btn btn-block btn-lg btn-success" value="{{ 'Login'|t }}">
</form>
{% if error %}
<div id="err" class="alert alert-danger m-3" role="alert">
<h4 class="alert-heading">{{ error.text|t }}</h4>
<p>{{ 'Error'|t}} {{ 'code'|t}} {{ error.code }}. {{ 'Check the entered credentials and make sure they are correct'|t }}.</p>
<img src='{{ urlsoftware }}/resources/images/{{ error_image }}'></img>
</div>
<script>
const noSaveData = 'connection' in navigator === false || ('connection' in navigator && !navigator.connection.saveData);
if (noSaveData) {
let playerScript = document.createElement("script");
playerScript.setAttribute("src", "{{ urlsoftware }}/resources/dist/{{ resource('players.js') }}");
playerScript.setAttribute("async", "false");
playerScript.onload = function() {
console.log("player script loaded");
var sound = new Howl({
src: ['{{ urlsoftware }}/resources/sounds/{{ error_sound }}'],
autoplay: true
});
sound.play();
};
document.head.insertBefore(playerScript, document.head.firstElementChild);
}
$("#err").delay(20000).fadeOut(300);
</script>
{% endif %}
</div>
<br><br>

View File

@ -21,10 +21,10 @@
</div>
</div>
</div>
<script>
<script nonce="{{ nonce }}">
$('#schedulesModal').on('show.bs.modal', function (event) {
//$( ".modal-body" ).loading("show");
$(".modal-body").load("modal_availability_schedule.php", {
$(".modal-body").load("modal_availability_schedule.php?nonce={{ nonce }}", {
orientation: window.innerHeight > window.innerWidth ? "portrait" : "landscape"
}, function() {
init_modal();
@ -69,7 +69,7 @@
<tbody id="table_body">
</tbody>
</table>
<script>
<script nonce="{{ nonce }}">
allertaJS.main.loadTable({tablePage: "list", useCustomTableEngine: "default"});
</script>
</div>

View File

@ -18,7 +18,7 @@
<tbody id="table_body">
</tbody>
</table>
<script>
<script nonce="{{ nonce }}">
allertaJS.main.loadTable({tablePage: "log", setInterval: false, callback: function(tableDt) {
tableDt
.column( '3:visible' )

View File

@ -25,7 +25,7 @@
<tbody id="table_body">
</tbody>
</table>
<script>
<script nonce="{{ nonce }}">
allertaJS.main.loadTable("list", true, 20000, true);
</script>
</div>

View File

@ -35,7 +35,7 @@
<tbody id="table_body">
</tbody>
</table>
<script>
<script nonce="{{ nonce }}">
allertaJS.main.loadTable({tablePage: "services", interval: 120000});
</script>
</div>

View File

@ -33,7 +33,7 @@
<tbody id="table_body">
</tbody>
</table>
<script>
<script nonce="{{ nonce }}">
allertaJS.main.loadTable({tablePage: "trainings", interval: 120000});
</script>
</div>

View File

@ -2,6 +2,8 @@
require_once 'core.php';
init_class();
$nonce = $tools->script_nonce;
if(!is_null($debugbar)){
$enable_debugbar = true;
$debugbarRenderer = $debugbar->getJavascriptRenderer("./vendor/maximebf/debugbar/src/DebugBar/Resources");
@ -20,7 +22,7 @@ $webpack_manifest = json_decode(
true
);
if(isset($_COOKIE["JSless"])){
if(isset($_COOKIE["JSless"]) && $_COOKIE["JSless"]){
$templates_dir = $_COOKIE["JSless"] ? "templates/JSless" : "templates";
$JSless = true;
} else {
@ -29,9 +31,15 @@ if(isset($_COOKIE["JSless"])){
}
if(isset($_GET["JSless"])){
setcookie("JSless", $_GET["JSless"] ? true : false, time() + (86400 * 365));
$templates_dir = $_GET["JSless"] ? "templates/JSless" : "templates";
$JSless = true;
if($_GET["JSless"]){
setcookie("JSless", true, time() + (86400 * 365));
$templates_dir = "templates/JSless";
$JSless = true;
} else {
setcookie("JSless", null, time() - 3600);
$templates_dir = "templates";
$JSless = false;
}
}
try {
@ -88,11 +96,11 @@ $twig->addFunction($function_resource);
$function_script = new \Twig\TwigFunction(
'script', function ($file, $onLoad=false) {
global $url_software, $webpack_manifest;
global $nonce, $url_software, $webpack_manifest;
$script_url = $url_software . "/resources/dist/" . $webpack_manifest[$file]["src"];
$script_integrity = $webpack_manifest[$file]["integrity"];
$script_tag = "<script src='{$script_url}' integrity='{$script_integrity}' crossorigin='anonymous'";
$script_tag = "<script src='{$script_url}' integrity='{$script_integrity}' crossorigin='anonymous' nonce='".$nonce."'";
if($onLoad){
$script_tag .= " onload='{$onLoad}'";
}
@ -133,11 +141,12 @@ p_stop();
$template = null;
function loadtemplate($templatename, $data, $requirelogin=true)
{
global $url_software, $user, $twig, $template, $enable_debugbar, $debugbarRenderer;
global $nonce, $url_software, $user, $twig, $template, $enable_debugbar, $debugbarRenderer;
p_start("Render Twig template");
if($requirelogin) {
$user->requirelogin();
}
$data['nonce'] = $nonce;
$data['delete_caches'] = isset($_GET["deleteCache"]) || isset($_GET["unregisterSW"]) || isset($_GET["unregisterSWandDisable"]);
$data['delete_service_workers'] = isset($_GET["unregisterSW"]) || isset($_GET["unregisterSWandDisable"]);
$data['delete_service_workers_and_disable'] = isset($_GET["unregisterSWandDisable"]);