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