1<?php 2 3use ComboStrap\LogUtility; 4use ComboStrap\Site; 5use ComboStrap\StringUtility; 6 7 8/** 9 * 10 * Adding security directive 11 * 12 */ 13class action_plugin_combo_metacsp extends DokuWiki_Action_Plugin 14{ 15 16 17 function __construct() 18 { 19 // enable direct access to language strings 20 // ie $this->lang 21 $this->setupLocale(); 22 } 23 24 public function register(Doku_Event_Handler $controller) 25 { 26 27 $controller->register_hook('DOKUWIKI_STARTED', 'BEFORE', $this, 'httpHeaderCsp', array()); 28 $controller->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', $this, 'htmlMetaCsp', array()); 29 30 } 31 32 /** 33 * Dokuwiki has already a canonical methodology 34 * https://www.dokuwiki.org/canonical 35 * 36 * @param $event 37 */ 38 function htmlMetaCsp($event) 39 { 40 41 42 /** 43 * HTML meta directives 44 */ 45 $directives = [ 46 'block-all-mixed-content', // no http, https 47 ]; 48 49 // Search if the CSP property is already present 50 $cspKey = null; 51 foreach ($event->data['meta'] as $key => $meta) { 52 if (isset($meta["http-equiv"])) { 53 if ($meta["http-equiv"] == "content-security-policy") { 54 $cspKey = $key; 55 } 56 } 57 } 58 if ($cspKey != null) { 59 $actualDirectives = StringUtility::explodeAndTrim($event->data['meta'][$cspKey]["content"], ","); 60 $directives = array_merge($actualDirectives, $directives); 61 $event->data['meta'][$cspKey] = [ 62 "http-equiv" => "content-security-policy", 63 "content" => join(", ", $directives) 64 ]; 65 } else { 66 $event->data['meta'][] = [ 67 "http-equiv" => "content-security-policy", 68 "content" => join(",", $directives) 69 ]; 70 } 71 72 } 73 74 function httpHeaderCsp($event) 75 { 76 /** 77 * Http header CSP directives 78 */ 79 $httpHeaderReferer = $_SERVER['HTTP_REFERER'] ?? ''; 80 $httpDirectives = []; 81 if (strpos($httpHeaderReferer, Site::getBaseUrl()) === false) { 82 // not same origin 83 $httpDirectives = [ 84 // the page cannot be used in a iframe (clickjacking), 85 "content-security-policy: frame-ancestors 'none'", 86 // the page cannot be used in a iframe (clickjacking) - deprecated for frame ancestores 87 // indicate whether or not a browser should be allowed to render 88 // a page in a <frame>, <iframe>, <embed> or <object>. Sites can use this to avoid 89 // click-jacking attacks, by ensuring that their content is not embedded into other sites. 90 "X-Frame-Options: SAMEORIGIN", 91 // stops a browser from trying to MIME-sniff the content type and forces it to stick with the declared content-type 92 "X-Content-Type-Options: nosniff", 93 // sends the origin if cross origin otherwise the full refer for same origin 94 "Referrer-Policy: strict-origin-when-cross-origin", 95 // 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. 96 "X-DNS-Prefetch-Control: on", 97 // 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. 98 "X-XSS-Protection: 1; mode=block" 99 ]; 100 } 101 if (!headers_sent()) { 102 foreach ($httpDirectives as $httpDirective) { 103 header($httpDirective); 104 } 105 } else { 106 LogUtility::msg("HTTP Headers have already ben sent. We couldn't add the CSP security header", LogUtility::LVL_MSG_WARNING, "security"); 107 } 108 } 109 110} 111