From 7802fd33a627dc7c582df871cfa613d9be8f8788 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Sat, 7 Dec 2019 19:29:23 +0100 Subject: tec: Allow to change CSP header from controllers For an extension, I needed to call a script from an external domain. Unfortunately, the CSP headers didn't allow this domain and I had to patch manually the FreshRSS FrontController for my extension. It's obviously not a long-term solution since it has nothing to do in the core of FRSS, and I don't want to apply this patch manually at each update. With this patch, I allow changing the CSP header from inside the controller actions. It allows extensions to modify headers. It's also an opportunity to remove a bit of code from the FrontController. I wasn't happy with the previous implementation anyhow. Reference: https://github.com/flusio/xExtension-Flus/commit/ed12d56#diff-ff12e33ed31b23bda327499fa6e84eccR143 --- lib/Minz/ActionController.php | 36 ++++++++++++++++++++++++++++++++++++ lib/Minz/Dispatcher.php | 1 + 2 files changed, 37 insertions(+) (limited to 'lib') diff --git a/lib/Minz/ActionController.php b/lib/Minz/ActionController.php index 123b9054c..f60b5411d 100644 --- a/lib/Minz/ActionController.php +++ b/lib/Minz/ActionController.php @@ -9,6 +9,9 @@ */ class Minz_ActionController { protected $view; + private $csp_policies = array( + 'default-src' => "'self'", + ); /** * Constructeur @@ -27,6 +30,39 @@ class Minz_ActionController { return $this->view; } + /** + * Set CSP policies. + * + * A default-src directive should always be given. + * + * References: + * - https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP + * - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/default-src + * + * @param array $policies An array where keys are directives and values are sources. + */ + protected function _csp($policies) { + if (!isset($policies['default-src'])) { + $action = Minz_Request::controllerName() . '#' . Minz_Request::actionName(); + Minz_Log::warning( + "Default CSP policy is not declared for action {$action}.", + ADMIN_LOG + ); + } + $this->csp_policies = $policies; + } + + /** + * Send HTTP Content-Security-Policy header based on declared policies. + */ + public function declareCspHeader() { + $policies = []; + foreach ($this->csp_policies as $directive => $sources) { + $policies[] = $directive . ' ' . $sources; + } + header('Content-Security-Policy: ' . implode('; ', $policies)); + } + /** * Méthodes à redéfinir (ou non) par héritage * firstAction est la première méthode exécutée par le Dispatcher diff --git a/lib/Minz/Dispatcher.php b/lib/Minz/Dispatcher.php index f05b285b5..7a1c3e1d7 100644 --- a/lib/Minz/Dispatcher.php +++ b/lib/Minz/Dispatcher.php @@ -50,6 +50,7 @@ class Minz_Dispatcher { $this->controller->lastAction (); if (!self::$needsReset) { + $this->controller->declareCspHeader(); $this->controller->view ()->build (); } } catch (Minz_Exception $e) { -- cgit v1.2.3