Allow Enabling Strict Content Security Policy (CSP) (#5059)
This commit is contained in:
commit
511f507c60
application
doc
library/Icinga
|
@ -96,7 +96,18 @@ class ConfigController extends Controller
|
|||
$this->assertPermission('config/general');
|
||||
$form = new GeneralConfigForm();
|
||||
$form->setIniConfig(Config::app());
|
||||
$form->handleRequest();
|
||||
$form->setOnSuccess(function (GeneralConfigForm $form) {
|
||||
$config = Config::app();
|
||||
$useStrictCsp = (bool) $config->get('security', 'use_strict_csp', false);
|
||||
if ($form->onSuccess() === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$appConfigForm = $form->getSubForm('form_config_general_application');
|
||||
if ($appConfigForm && (bool) $appConfigForm->getValue('security_use_strict_csp') !== $useStrictCsp) {
|
||||
$this->getResponse()->setReloadWindow(true);
|
||||
}
|
||||
})->handleRequest();
|
||||
|
||||
$this->view->form = $form;
|
||||
$this->view->title = $this->translate('General');
|
||||
|
|
|
@ -55,6 +55,18 @@ class ApplicationConfigForm extends Form
|
|||
)
|
||||
);
|
||||
|
||||
$this->addElement(
|
||||
'checkbox',
|
||||
'security_use_strict_csp',
|
||||
[
|
||||
'label' => $this->translate('Enable strict content security policy'),
|
||||
'description' => $this->translate(
|
||||
'Set whether to to use strict content security policy (CSP).'
|
||||
. ' This setting helps to protect from cross-site scripting (XSS).'
|
||||
)
|
||||
]
|
||||
);
|
||||
|
||||
$this->addElement(
|
||||
'text',
|
||||
'global_module_path',
|
||||
|
|
|
@ -40,6 +40,19 @@ config_resource = "icingaweb_db"
|
|||
module_path = "/usr/share/icingaweb2/modules"
|
||||
```
|
||||
|
||||
### Security Configuration <a id="configuration-general-security"></a>
|
||||
|
||||
| Option | Description |
|
||||
|------------------|------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| use\_strict\_csp | **Optional.** Set this to `1` to enable strict [Content Security Policy](20-Advanced-Topics.md#advanced-topics-security-csp). Defaults to `0`. |
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
[security]
|
||||
use_strict_csp = "1"
|
||||
```
|
||||
|
||||
### Logging Configuration <a id="configuration-general-logging"></a>
|
||||
|
||||
Option | Description
|
||||
|
|
|
@ -4,6 +4,7 @@ This chapter provides details for advanced Icinga Web 2 topics.
|
|||
|
||||
* [Global URL parameters](20-Advanced-Topics.md#global-url-parameters)
|
||||
* [VirtualHost configuration](20-Advanced-Topics.md#virtualhost-configuration)
|
||||
* [Content Security Policy (CSP)](20-Advanced-Topics.md#advanced-topics-csp)
|
||||
* [Advanced Authentication Tips](20-Advanced-Topics.md#advanced-topics-authentication-tips)
|
||||
* [Source installation](20-Advanced-Topics.md#installing-from-source)
|
||||
* [Automated setup](20-Advanced-Topics.md#web-setup-automation)
|
||||
|
@ -115,6 +116,37 @@ Reload Apache and open the FQDN in your web browser.
|
|||
systemctl reload httpd
|
||||
```
|
||||
|
||||
### Content Security Policy (CSP) <a id="advanced-topics-csp"></a>
|
||||
|
||||
Elevate your security standards to an even higher level by enabling the [Content Security Policy (CSP)](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) for Icinga Web.
|
||||
Enabling strict CSP can prevent your Icinga Web environment from becoming a potential target of [Cross-Site Scripting (XSS)](https://developer.mozilla.org/en-US/docs/Glossary/Cross-site_scripting)
|
||||
and data injection attacks. After enabling this feature Icinga Web defines all the required CSP headers. Subsequently,
|
||||
only content coming from Icinga Web's own origin is accepted, inline JS is prohibited, and inline CSS is accepted only
|
||||
if it contains the nonce set in the response header.
|
||||
|
||||
We decided against enabling this by default as we cannot guarantee that all the modules out there will function correctly.
|
||||
Therefore, you have to manually enable this policy explicitly and accept the risks that this might break some of
|
||||
the Icinga Web modules. Icinga Web and all it's components listed below, on the other hand, fully support strict CSP. If
|
||||
that's not the case, please submit an issue on GitHub in the respective repositories.
|
||||
|
||||
Here is a list of all Icinga Web components that are capable of strict CSP.
|
||||
|
||||
| Name | CSP supported since |
|
||||
|-----------------------------------|-------------------------------------------------------------------------------------------|
|
||||
| Icinga DB Web | [v1.1.0](https://github.com/Icinga/icingadb-web/releases/tag/v1.1.0) |
|
||||
| Icinga Reporting | [v1.0.0](https://github.com/Icinga/icingaweb2-module-reporting/releases/tag/v1.0.0) |
|
||||
| Icinga IDO Reports | [v0.10.1](https://github.com/Icinga/icingaweb2-module-idoreports/releases/tag/v0.10.1) |
|
||||
| Icinga Cube | [v1.3.2](https://github.com/Icinga/icingaweb2-module-cube/releases/tag/v1.3.2) |
|
||||
| Icinga Business Process Modeling | [v2.5.0](https://github.com/Icinga/icingaweb2-module-businessprocess/releases/tag/v2.5.0) |
|
||||
| Icinga Certificate Monitoring | [v1.3.0](https://github.com/Icinga/icingaweb2-module-x509/releases/tag/v1.3.0) |
|
||||
| Icinga PDF Export | [v0.10.2](https://github.com/Icinga/icingaweb2-module-pdfexport/releases/tag/v0.10.2) |
|
||||
| Icinga Web Jira Integration | [v1.3.2](https://github.com/Icinga/icingaweb2-module-jira/releases/tag/v1.3.2) |
|
||||
| Icinga Web Graphite Integration | [v1.3.0](https://github.com/Icinga/icingaweb2-module-graphite/releases/tag/v1.3.0) |
|
||||
| Icinga Web GenericTTS Integration | [v2.1.0](https://github.com/Icinga/icingaweb2-module-generictts/releases/tag/v2.1.0) |
|
||||
| Icinga Web Nagvis Integration | [v1.2.0](https://github.com/Icinga/icingaweb2-module-nagvis/releases/tag/v1.2.0) |
|
||||
| Icinga Web AWS Integration | [v1.1.0](https://github.com/Icinga/icingaweb2-module-aws/releases/tag/v1.1.0) |
|
||||
|
||||
|
||||
## Advanced Authentication Tips <a id="advanced-topics-authentication-tips"></a>
|
||||
|
||||
### Manual User Creation for Database Authentication Backend <a id="advanced-topics-authentication-tips-manual-user-database-auth"></a>
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
<?php
|
||||
|
||||
/* Icinga Web 2 | (c) 2023 Icinga GmbH | GPLv2+ */
|
||||
|
||||
namespace Icinga\Util;
|
||||
|
||||
use Icinga\Web\Response;
|
||||
use Icinga\Web\Window;
|
||||
use RuntimeException;
|
||||
|
||||
use function ipl\Stdlib\get_php_type;
|
||||
|
||||
/**
|
||||
* Helper to enable strict content security policy (CSP)
|
||||
*
|
||||
* {@see static::addHeader()} adds a strict Content-Security-Policy header with a nonce to still support dynamic CSS
|
||||
* securely.
|
||||
* Note that {@see static::createNonce()} must be called first.
|
||||
* Use {@see static::getStyleNonce()} to access the nonce for dynamic CSS.
|
||||
*
|
||||
* A nonce is not created for dynamic JS,
|
||||
* and it is questionable whether this will ever be supported.
|
||||
*/
|
||||
class Csp
|
||||
{
|
||||
/** @var static */
|
||||
protected static $instance;
|
||||
|
||||
/** @var ?string */
|
||||
protected $styleNonce;
|
||||
|
||||
/** Singleton */
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Content-Security-Policy header with a nonce for dynamic CSS
|
||||
*
|
||||
* Note that {@see static::createNonce()} must be called beforehand.
|
||||
*
|
||||
* @param Response $response
|
||||
*
|
||||
* @throws RuntimeException If no nonce set for CSS
|
||||
*/
|
||||
public static function addHeader(Response $response): void
|
||||
{
|
||||
$csp = static::getInstance();
|
||||
|
||||
if (empty($csp->styleNonce)) {
|
||||
throw new RuntimeException('No nonce set for CSS');
|
||||
}
|
||||
|
||||
$response->setHeader('Content-Security-Policy', "style-src 'self' 'nonce-$csp->styleNonce';", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set/recreate nonce for dynamic CSS
|
||||
*
|
||||
* Should always be called upon initial page loads or page reloads,
|
||||
* as it sets/recreates a nonce for CSS and writes it to a window-aware session.
|
||||
*/
|
||||
public static function createNonce(): void
|
||||
{
|
||||
$csp = static::getInstance();
|
||||
$csp->styleNonce = base64_encode(random_bytes(16));
|
||||
|
||||
Window::getInstance()->getSessionNamespace('csp')->set('style_nonce', $csp->styleNonce);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get nonce for dynamic CSS
|
||||
*
|
||||
* @return ?string
|
||||
*/
|
||||
public static function getStyleNonce(): ?string
|
||||
{
|
||||
return static::getInstance()->styleNonce;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the CSP instance
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
protected static function getInstance(): self
|
||||
{
|
||||
if (static::$instance === null) {
|
||||
$csp = new static();
|
||||
$nonce = Window::getInstance()->getSessionNamespace('csp')->get('style_nonce');
|
||||
if ($nonce !== null && ! is_string($nonce)) {
|
||||
throw new RuntimeException(
|
||||
sprintf(
|
||||
'Nonce value is expected to be string, got %s instead',
|
||||
get_php_type($nonce)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$csp->styleNonce = $nonce;
|
||||
|
||||
static::$instance = $csp;
|
||||
}
|
||||
|
||||
return static::$instance;
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ namespace Icinga\Web\Controller;
|
|||
use Icinga\Application\Modules\Module;
|
||||
use Icinga\Common\PdfExport;
|
||||
use Icinga\File\Pdf;
|
||||
use Icinga\Util\Csp;
|
||||
use Icinga\Web\View;
|
||||
use ipl\I18n\Translation;
|
||||
use Zend_Controller_Action;
|
||||
|
@ -171,6 +172,10 @@ class ActionController extends Zend_Controller_Action
|
|||
$this->redirectToLogin(Url::fromRequest());
|
||||
}
|
||||
|
||||
if (! $this->isXhr() && $this->Config()->get('security', 'use_strict_csp', false)) {
|
||||
Csp::createNonce();
|
||||
}
|
||||
|
||||
$this->view->tabs = new Tabs();
|
||||
$this->prepareInit();
|
||||
$this->init();
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
namespace Icinga\Web;
|
||||
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\Util\Csp;
|
||||
use Zend_Controller_Response_Http;
|
||||
use Icinga\Application\Icinga;
|
||||
use Icinga\Web\Response\JsonResponse;
|
||||
|
@ -370,6 +372,10 @@ class Response extends Zend_Controller_Response_Http
|
|||
if ($redirectUrl !== null) {
|
||||
$this->setRedirect($redirectUrl->getAbsoluteUrl());
|
||||
}
|
||||
|
||||
if (Csp::getStyleNonce() && Config::app()->get('security', 'use_strict_csp', false)) {
|
||||
Csp::addHeader($this);
|
||||
}
|
||||
}
|
||||
|
||||
if (! $this->getHeader('Content-Type', true)) {
|
||||
|
|
Loading…
Reference in New Issue