121913ab3SNickeau<?php 221913ab3SNickeau 3e8b2ff59SNickeauuse ComboStrap\LogUtility; 4531e725cSNickeauuse ComboStrap\Site; 521913ab3SNickeauuse ComboStrap\StringUtility; 621913ab3SNickeau 721913ab3SNickeau 821913ab3SNickeau/** 921913ab3SNickeau * 1021913ab3SNickeau * Adding security directive 1121913ab3SNickeau * 1221913ab3SNickeau */ 1321913ab3SNickeauclass action_plugin_combo_metacsp extends DokuWiki_Action_Plugin 1421913ab3SNickeau{ 1521913ab3SNickeau 1621913ab3SNickeau 1721913ab3SNickeau function __construct() 1821913ab3SNickeau { 1921913ab3SNickeau // enable direct access to language strings 2021913ab3SNickeau // ie $this->lang 2121913ab3SNickeau $this->setupLocale(); 2221913ab3SNickeau } 2321913ab3SNickeau 2421913ab3SNickeau public function register(Doku_Event_Handler $controller) 2521913ab3SNickeau { 26e8b2ff59SNickeau 27e8b2ff59SNickeau $controller->register_hook('DOKUWIKI_STARTED', 'BEFORE', $this, 'httpHeaderCsp', array()); 28e8b2ff59SNickeau $controller->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', $this, 'htmlMetaCsp', array()); 29e8b2ff59SNickeau 3021913ab3SNickeau } 3121913ab3SNickeau 3221913ab3SNickeau /** 3321913ab3SNickeau * Dokuwiki has already a canonical methodology 3421913ab3SNickeau * https://www.dokuwiki.org/canonical 3521913ab3SNickeau * 3621913ab3SNickeau * @param $event 3721913ab3SNickeau */ 38e8b2ff59SNickeau function htmlMetaCsp($event) 3921913ab3SNickeau { 4021913ab3SNickeau 41531e725cSNickeau 42531e725cSNickeau /** 43531e725cSNickeau * HTML meta directives 44531e725cSNickeau */ 4521913ab3SNickeau $directives = [ 4621913ab3SNickeau 'block-all-mixed-content', // no http, https 4721913ab3SNickeau ]; 4821913ab3SNickeau 4921913ab3SNickeau // Search if the CSP property is already present 5021913ab3SNickeau $cspKey = null; 5121913ab3SNickeau foreach ($event->data['meta'] as $key => $meta) { 5221913ab3SNickeau if (isset($meta["http-equiv"])) { 5385e82846SNickeau if ($meta["http-equiv"] == "content-security-policy") { 5421913ab3SNickeau $cspKey = $key; 5521913ab3SNickeau } 5621913ab3SNickeau } 5721913ab3SNickeau } 5821913ab3SNickeau if ($cspKey != null) { 5921913ab3SNickeau $actualDirectives = StringUtility::explodeAndTrim($event->data['meta'][$cspKey]["content"], ","); 6021913ab3SNickeau $directives = array_merge($actualDirectives, $directives); 6121913ab3SNickeau $event->data['meta'][$cspKey] = [ 6285e82846SNickeau "http-equiv" => "content-security-policy", 6321913ab3SNickeau "content" => join(", ", $directives) 6421913ab3SNickeau ]; 6521913ab3SNickeau } else { 6621913ab3SNickeau $event->data['meta'][] = [ 6785e82846SNickeau "http-equiv" => "content-security-policy", 6821913ab3SNickeau "content" => join(",", $directives) 6921913ab3SNickeau ]; 7021913ab3SNickeau } 7121913ab3SNickeau 72e8b2ff59SNickeau } 73e8b2ff59SNickeau 74e8b2ff59SNickeau function httpHeaderCsp($event) 75e8b2ff59SNickeau { 76531e725cSNickeau /** 77531e725cSNickeau * Http header CSP directives 78531e725cSNickeau */ 79*1b6a1c16Sgerardnico $httpHeaderReferer = $_SERVER['HTTP_REFERER'] ?? ''; 80531e725cSNickeau $httpDirectives = []; 81*1b6a1c16Sgerardnico if (strpos($httpHeaderReferer, Site::getBaseUrl()) === false) { 82531e725cSNickeau // not same origin 8321913ab3SNickeau $httpDirectives = [ 84c3437056SNickeau // the page cannot be used in a iframe (clickjacking), 85c3437056SNickeau "content-security-policy: frame-ancestors 'none'", 86c3437056SNickeau // the page cannot be used in a iframe (clickjacking) - deprecated for frame ancestores 8704fd306cSNickeau // indicate whether or not a browser should be allowed to render 8804fd306cSNickeau // a page in a <frame>, <iframe>, <embed> or <object>. Sites can use this to avoid 8904fd306cSNickeau // click-jacking attacks, by ensuring that their content is not embedded into other sites. 90c3437056SNickeau "X-Frame-Options: SAMEORIGIN", 91c3437056SNickeau // stops a browser from trying to MIME-sniff the content type and forces it to stick with the declared content-type 92c3437056SNickeau "X-Content-Type-Options: nosniff", 93c3437056SNickeau // sends the origin if cross origin otherwise the full refer for same origin 94c3437056SNickeau "Referrer-Policy: strict-origin-when-cross-origin", 95c3437056SNickeau // 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. 96c3437056SNickeau "X-DNS-Prefetch-Control: on", 97c3437056SNickeau // 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. 98c3437056SNickeau "X-XSS-Protection: 1; mode=block" 9921913ab3SNickeau ]; 100531e725cSNickeau } 101e8b2ff59SNickeau if (!headers_sent()) { 10221913ab3SNickeau foreach ($httpDirectives as $httpDirective) { 10321913ab3SNickeau header($httpDirective); 10421913ab3SNickeau } 105e8b2ff59SNickeau } else { 106e8b2ff59SNickeau LogUtility::msg("HTTP Headers have already ben sent. We couldn't add the CSP security header", LogUtility::LVL_MSG_WARNING, "security"); 107e8b2ff59SNickeau } 10821913ab3SNickeau } 10921913ab3SNickeau 11021913ab3SNickeau} 111