121913ab3SNickeau<?php 221913ab3SNickeau 3*e8b2ff59SNickeauuse 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 { 27*e8b2ff59SNickeau 28*e8b2ff59SNickeau $controller->register_hook('DOKUWIKI_STARTED', 'BEFORE', $this, 'httpHeaderCsp', array()); 29*e8b2ff59SNickeau $controller->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', $this, 'htmlMetaCsp', array()); 30*e8b2ff59SNickeau 3121913ab3SNickeau } 3221913ab3SNickeau 3321913ab3SNickeau /** 3421913ab3SNickeau * Dokuwiki has already a canonical methodology 3521913ab3SNickeau * https://www.dokuwiki.org/canonical 3621913ab3SNickeau * 3721913ab3SNickeau * @param $event 3821913ab3SNickeau */ 39*e8b2ff59SNickeau 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 73*e8b2ff59SNickeau } 74*e8b2ff59SNickeau 75*e8b2ff59SNickeau function httpHeaderCsp($event) 76*e8b2ff59SNickeau { 77531e725cSNickeau /** 78531e725cSNickeau * Http header CSP directives 79531e725cSNickeau */ 80531e725cSNickeau $httpHeaderReferer = $_SERVER['HTTP_REFERER']; 81531e725cSNickeau $httpDirectives = []; 82531e725cSNickeau if (strpos($httpHeaderReferer, Site::getUrl()) === false) { 83531e725cSNickeau // not same origin 8421913ab3SNickeau $httpDirectives = [ 8585e82846SNickeau "content-security-policy: frame-ancestors 'none'", // the page cannot be used in a iframe (clickjacking), 8621913ab3SNickeau "X-Frame-Options: deny" // the page cannot be used in a iframe (clickjacking) - deprecated for frame ancestores 8721913ab3SNickeau ]; 88531e725cSNickeau } 89*e8b2ff59SNickeau if (!headers_sent()) { 9021913ab3SNickeau foreach ($httpDirectives as $httpDirective) { 9121913ab3SNickeau header($httpDirective); 9221913ab3SNickeau } 93*e8b2ff59SNickeau } else { 94*e8b2ff59SNickeau LogUtility::msg("HTTP Headers have already ben sent. We couldn't add the CSP security header", LogUtility::LVL_MSG_WARNING,"security"); 95*e8b2ff59SNickeau } 9621913ab3SNickeau } 9721913ab3SNickeau 9821913ab3SNickeau} 99