121913ab3SNickeau<?php 221913ab3SNickeau 3e8b2ff59SNickeauuse ComboStrap\LogUtility; 4531e725cSNickeauuse ComboStrap\Site; 521913ab3SNickeauuse ComboStrap\StringUtility; 621913ab3SNickeau 721913ab3SNickeauif (!defined('DOKU_INC')) die(); 821913ab3SNickeau 921913ab3SNickeau/** 1021913ab3SNickeau * 1121913ab3SNickeau * Adding security directive 1221913ab3SNickeau * 1321913ab3SNickeau */ 1421913ab3SNickeauclass action_plugin_combo_metacsp extends DokuWiki_Action_Plugin 1521913ab3SNickeau{ 1621913ab3SNickeau 1721913ab3SNickeau 1821913ab3SNickeau function __construct() 1921913ab3SNickeau { 2021913ab3SNickeau // enable direct access to language strings 2121913ab3SNickeau // ie $this->lang 2221913ab3SNickeau $this->setupLocale(); 2321913ab3SNickeau } 2421913ab3SNickeau 2521913ab3SNickeau public function register(Doku_Event_Handler $controller) 2621913ab3SNickeau { 27e8b2ff59SNickeau 28e8b2ff59SNickeau $controller->register_hook('DOKUWIKI_STARTED', 'BEFORE', $this, 'httpHeaderCsp', array()); 29e8b2ff59SNickeau $controller->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', $this, 'htmlMetaCsp', array()); 30e8b2ff59SNickeau 3121913ab3SNickeau } 3221913ab3SNickeau 3321913ab3SNickeau /** 3421913ab3SNickeau * Dokuwiki has already a canonical methodology 3521913ab3SNickeau * https://www.dokuwiki.org/canonical 3621913ab3SNickeau * 3721913ab3SNickeau * @param $event 3821913ab3SNickeau */ 39e8b2ff59SNickeau function htmlMetaCsp($event) 4021913ab3SNickeau { 4121913ab3SNickeau 42531e725cSNickeau 43531e725cSNickeau /** 44531e725cSNickeau * HTML meta directives 45531e725cSNickeau */ 4621913ab3SNickeau $directives = [ 4721913ab3SNickeau 'block-all-mixed-content', // no http, https 4821913ab3SNickeau ]; 4921913ab3SNickeau 5021913ab3SNickeau // Search if the CSP property is already present 5121913ab3SNickeau $cspKey = null; 5221913ab3SNickeau foreach ($event->data['meta'] as $key => $meta) { 5321913ab3SNickeau if (isset($meta["http-equiv"])) { 5485e82846SNickeau if ($meta["http-equiv"] == "content-security-policy") { 5521913ab3SNickeau $cspKey = $key; 5621913ab3SNickeau } 5721913ab3SNickeau } 5821913ab3SNickeau } 5921913ab3SNickeau if ($cspKey != null) { 6021913ab3SNickeau $actualDirectives = StringUtility::explodeAndTrim($event->data['meta'][$cspKey]["content"], ","); 6121913ab3SNickeau $directives = array_merge($actualDirectives, $directives); 6221913ab3SNickeau $event->data['meta'][$cspKey] = [ 6385e82846SNickeau "http-equiv" => "content-security-policy", 6421913ab3SNickeau "content" => join(", ", $directives) 6521913ab3SNickeau ]; 6621913ab3SNickeau } else { 6721913ab3SNickeau $event->data['meta'][] = [ 6885e82846SNickeau "http-equiv" => "content-security-policy", 6921913ab3SNickeau "content" => join(",", $directives) 7021913ab3SNickeau ]; 7121913ab3SNickeau } 7221913ab3SNickeau 73e8b2ff59SNickeau } 74e8b2ff59SNickeau 75e8b2ff59SNickeau function httpHeaderCsp($event) 76e8b2ff59SNickeau { 77531e725cSNickeau /** 78531e725cSNickeau * Http header CSP directives 79531e725cSNickeau */ 80531e725cSNickeau $httpHeaderReferer = $_SERVER['HTTP_REFERER']; 81531e725cSNickeau $httpDirectives = []; 82*c3437056SNickeau if (strpos($httpHeaderReferer, Site::getBaseUrl()) === false) { 83531e725cSNickeau // not same origin 8421913ab3SNickeau $httpDirectives = [ 85*c3437056SNickeau // the page cannot be used in a iframe (clickjacking), 86*c3437056SNickeau "content-security-policy: frame-ancestors 'none'", 87*c3437056SNickeau // the page cannot be used in a iframe (clickjacking) - deprecated for frame ancestores 88*c3437056SNickeau "X-Frame-Options: SAMEORIGIN", 89*c3437056SNickeau // stops a browser from trying to MIME-sniff the content type and forces it to stick with the declared content-type 90*c3437056SNickeau "X-Content-Type-Options: nosniff", 91*c3437056SNickeau // sends the origin if cross origin otherwise the full refer for same origin 92*c3437056SNickeau "Referrer-Policy: strict-origin-when-cross-origin", 93*c3437056SNickeau // controls DNS prefetching, allowing browsers to proactively perform domain name resolution on external links, images, CSS, JavaScript, and more. This prefetching is performed in the background, so the DNS is more likely to be resolved by the time the referenced items are needed. This reduces latency when the user clicks a link. 94*c3437056SNickeau "X-DNS-Prefetch-Control: on", 95*c3437056SNickeau // This header stops pages from loading when they detect reflected cross-site scripting (XSS) attacks. Although this protection is not necessary when sites implement a strong Content-Security-Policy disabling the use of inline JavaScript ('unsafe-inline'), it can still provide protection for older web browsers that don't support CSP. 96*c3437056SNickeau "X-XSS-Protection: 1; mode=block" 9721913ab3SNickeau ]; 98531e725cSNickeau } 99e8b2ff59SNickeau if (!headers_sent()) { 10021913ab3SNickeau foreach ($httpDirectives as $httpDirective) { 10121913ab3SNickeau header($httpDirective); 10221913ab3SNickeau } 103e8b2ff59SNickeau } else { 104e8b2ff59SNickeau LogUtility::msg("HTTP Headers have already ben sent. We couldn't add the CSP security header", LogUtility::LVL_MSG_WARNING,"security"); 105e8b2ff59SNickeau } 10621913ab3SNickeau } 10721913ab3SNickeau 10821913ab3SNickeau} 109