mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-07-08 22:18:28 +03:00
Redesign \SnappyMail\HTTP\CSP for better control
This commit is contained in:
parent
915a142315
commit
bb77d0ae02
3 changed files with 44 additions and 53 deletions
|
|
@ -6,9 +6,9 @@ class RecaptchaPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
NAME = 'reCaptcha',
|
||||
AUTHOR = 'SnappyMail',
|
||||
URL = 'https://snappymail.eu/',
|
||||
VERSION = '2.14',
|
||||
RELEASE = '2023-01-11',
|
||||
REQUIRED = '2.23',
|
||||
VERSION = '2.15',
|
||||
RELEASE = '2023-02-22',
|
||||
REQUIRED = '2.26.4',
|
||||
CATEGORY = 'General',
|
||||
LICENSE = 'MIT',
|
||||
DESCRIPTION = 'A CAPTCHA (v2) is a program that can generate and grade tests that humans can pass but current computer programs cannot. For example, humans can read distorted text as the one shown below, but current computer programs can\'t. More info at https://developers.google.com/recaptcha';
|
||||
|
|
@ -141,12 +141,9 @@ class RecaptchaPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
|
||||
public function ContentSecurityPolicy(\SnappyMail\HTTP\CSP $CSP)
|
||||
{
|
||||
// $CSP->script[] = 'https://www.google.com/recaptcha/';
|
||||
$CSP->script[] = 'https://www.gstatic.com/recaptcha/';
|
||||
$CSP->script[] = 'https://www.recaptcha.net/recaptcha/';
|
||||
// $CSP->frame[] = 'https://www.google.com/recaptcha/';
|
||||
// $CSP->frame[] = 'https://recaptcha.google.com/recaptcha/';
|
||||
$CSP->frame[] = 'https://www.recaptcha.net/recaptcha/';
|
||||
$CSP->add('script-src', 'https://www.gstatic.com/recaptcha/');
|
||||
$CSP->add('script-src', 'https://www.recaptcha.net/recaptcha/');
|
||||
$CSP->add('frame-src', 'https://www.recaptcha.net/recaptcha/');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,15 +41,14 @@ abstract class Api
|
|||
$CSP = new \SnappyMail\HTTP\CSP(\trim($oConfig->Get('security', 'content_security_policy', '')));
|
||||
$CSP->report = $oConfig->Get('security', 'csp_report', false);
|
||||
$CSP->report_only = $oConfig->Get('debug', 'enable', false); // || SNAPPYMAIL_DEV
|
||||
// $CSP->frame = \explode(' ', $oConfig->Get('security', 'csp_frame', ''));
|
||||
|
||||
// Allow https: due to remote images in e-mails or use proxy
|
||||
if (!$oConfig->Get('security', 'use_local_proxy_for_external_images', '')) {
|
||||
$CSP->img[] = 'https:';
|
||||
$CSP->img[] = 'http:';
|
||||
$CSP->add('img-src', 'https:');
|
||||
$CSP->add('img-src', 'http:');
|
||||
}
|
||||
if ($sScriptNonce) {
|
||||
$CSP->script[] = "'nonce-{$sScriptNonce}'";
|
||||
$CSP->add('script-src', "'nonce-{$sScriptNonce}'");
|
||||
}
|
||||
|
||||
static::Actions()->Plugins()->RunHook('main.content-security-policy', array($CSP));
|
||||
|
|
|
|||
|
|
@ -8,64 +8,59 @@ namespace SnappyMail\HTTP;
|
|||
class CSP
|
||||
{
|
||||
public
|
||||
$base = ["'self'"],
|
||||
$default = ["'self'", 'data:'],
|
||||
// Knockout.js requires eval() for observable binding purposes
|
||||
// Safari < 15.4 does not support strict-dynamic
|
||||
// $script = ["'strict-dynamic'", "'unsafe-eval'"],
|
||||
$script = ["'self'", "'unsafe-eval'"],
|
||||
// Knockout.js requires unsafe-inline?
|
||||
// $script = ["'self'", "'unsafe-inline'", "'unsafe-eval'"],
|
||||
$img = ["'self'", 'data:'],
|
||||
$style = ["'self'", "'unsafe-inline'"],
|
||||
$frame = [],
|
||||
$frame_ancestors = [],
|
||||
|
||||
$report = false,
|
||||
$report_to = [],
|
||||
$report_only = false;
|
||||
|
||||
private $directives = [
|
||||
'base-uri' => ["'self'"],
|
||||
'default-src' => ["'self'", 'data:'],
|
||||
// Knockout.js requires eval() for observable binding purposes
|
||||
// Safari < 15.4 does not support strict-dynamic
|
||||
// 'script-src' => ["'strict-dynamic'", "'unsafe-eval'"],
|
||||
'script-src' => ["'self'", "'unsafe-eval'"],
|
||||
// Knockout.js requires unsafe-inline?
|
||||
// 'script-src' => ["'self'", "'unsafe-inline'", "'unsafe-eval'"],
|
||||
'img-src' => ["'self'", 'data:'],
|
||||
'style-src' => ["'self'", "'unsafe-inline'"],
|
||||
];
|
||||
|
||||
function __construct(string $default = '')
|
||||
{
|
||||
if ($default) {
|
||||
foreach (\explode(';', $default) as $directive) {
|
||||
$values = \preg_split('/\\s+/', $directive);
|
||||
$name = \str_replace('-', '_', \preg_replace('/-(src|uri)$/D', '', \trim(\array_shift($values))));
|
||||
$this->$name = \array_unique(\array_merge($this->$name, $values));
|
||||
$sources = \preg_split('/\\s+/', \trim($directive));
|
||||
$directive = \array_shift($sources);
|
||||
if (!isset($this->directives[$directive])) {
|
||||
$this->directives[$directive] = [];
|
||||
}
|
||||
$this->directives[$directive] = \array_merge($this->directives[$directive], $sources);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function __toString() : string
|
||||
{
|
||||
$params = [
|
||||
'base-uri ' . \implode(' ', \array_unique($this->base)),
|
||||
'default-src ' . \implode(' ', \array_unique($this->default))
|
||||
];
|
||||
if ($this->script) {
|
||||
$params[] = 'script-src ' . \implode(' ', \array_unique($this->script));
|
||||
}
|
||||
if ($this->img) {
|
||||
$params[] = 'img-src ' . \implode(' ', \array_unique($this->img));
|
||||
}
|
||||
if ($this->style) {
|
||||
$params[] = 'style-src ' . \implode(' ', \array_unique($this->style));
|
||||
}
|
||||
if ($this->frame) {
|
||||
$params[] = 'frame-src ' . \implode(' ', \array_unique($this->frame));
|
||||
}
|
||||
if ($this->frame_ancestors) {
|
||||
$params[] = 'frame-ancestors ' . \implode(' ', \array_unique($this->frame_ancestors));
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
// report-uri deprecated
|
||||
unset($this->directives['report-uri']);
|
||||
if ($this->report) {
|
||||
$params[] = 'report-uri ./?/CspReport';
|
||||
$this->directives['report-uri'] = [\RainLoop\Utils::WebPath() . '?/CspReport'];
|
||||
}
|
||||
$params = [];
|
||||
foreach ($this->directives as $directive => $sources) {
|
||||
$params[] = $directive . ' ' . \implode(' ', \array_unique($sources));
|
||||
}
|
||||
|
||||
return \implode('; ', $params);
|
||||
}
|
||||
|
||||
public function add(string $directive, string $source) : void
|
||||
{
|
||||
if (!isset($this->directives[$directive])) {
|
||||
$this->directives[$directive] = [];
|
||||
}
|
||||
$this->directives[$directive][] = $source;
|
||||
}
|
||||
|
||||
public function setHeaders() : void
|
||||
{
|
||||
if ($this->report_only) {
|
||||
|
|
@ -73,7 +68,7 @@ class CSP
|
|||
} else {
|
||||
\header('Content-Security-Policy: ' . $this);
|
||||
}
|
||||
if (!$this->frame_ancestors) {
|
||||
if (empty($this->directives['frame-ancestors'])) {
|
||||
\header('X-Frame-Options: DENY');
|
||||
} else {
|
||||
// \header('X-Frame-Options: SAMEORIGIN');
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue