Resolve #585 by allowing additional Sec-Fetch rules

This commit is contained in:
the-djmaze 2022-11-09 11:19:05 +01:00
parent 3992d9d5f7
commit 895abd09be
3 changed files with 42 additions and 3 deletions

View file

@ -194,7 +194,15 @@ class Application extends \RainLoop\Config\AbstractConfig
'content_security_policy' => array('', 'For example to allow all images use "img-src https:". More info at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy#directives'),
'csp_report' => array(false, 'Report CSP errors to PHP and/or SnappyMail Log'),
'encrypt_cipher' => array('aes-256-cbc-hmac-sha1', 'A valid cipher method from https://php.net/openssl_get_cipher_methods'),
'cookie_samesite' => array('Strict', 'Strict, Lax or None')
'cookie_samesite' => array('Strict', 'Strict, Lax or None'),
'secfetch_allow' => array('', 'Additional allowed Sec-Fetch combinations separated by ";".
For example:
* Allow iframe on same domain in any mode: dest=iframe,site=same-origin
* Allow navigate to iframe on same domain: mode=navigate,dest=iframe,site=same-origin
* Allow navigate to iframe on (sub)domain: mode=navigate,dest=iframe,site=same-site
* Allow navigate to iframe from any domain: mode=navigate,dest=iframe,site=cross-site
Default is "site=same-origin;site=none"')
),
'admin_panel' => array(

View file

@ -101,7 +101,7 @@ abstract class Service
$sResult = '';
if (\count($aPaths) && !empty($aPaths[0]) && 'index' !== \strtolower($aPaths[0]))
{
if ('mailto' !== \strtolower($aPaths[0]) && !\SnappyMail\HTTP\SecFetch::isSameOrigin()) {
if ('mailto' !== \strtolower($aPaths[0]) && !\SnappyMail\HTTP\SecFetch::matchAnyRule($oConfig->Get('security', 'secfetch_allow', ''))) {
\MailSo\Base\Http::StatusHeader(403);
echo $oServiceActions->ErrorTemplates('Access Denied.',
"Disallowed Sec-Fetch

View file

@ -107,11 +107,42 @@ abstract class SecFetch
public static function isSameOrigin() : bool
{
return !isset($_SERVER['HTTP_SEC_FETCH_SITE'])
|| 'same-origin' === $_SERVER['HTTP_SEC_FETCH_SITE']
// Fulguris incognito: Dest = document, Mode = navigate, Site = none, User = true
|| 'none' === $_SERVER['HTTP_SEC_FETCH_SITE'];
}
/**
* $rules = Additional allowed Sec-Fetch combinations separated by ";".
* For example:
* Allow iframe on same domain in any mode: dest=iframe,site=same-origin
* Allow navigate to iframe on same domain: mode=navigate,dest=iframe,site=same-origin
* Allow navigate to iframe on (sub)domain: mode=navigate,dest=iframe,site=same-site
* Allow navigate to iframe from any domain: mode=navigate,dest=iframe,site=cross-site
*
* Default is "site=same-origin;site=none"
*/
public static function matchAnyRule(string $rules) : bool
{
if (!isset($_SERVER['HTTP_SEC_FETCH_SITE'])) {
return true;
}
$secfetch = \explode(';', 'site=same-origin;site=none;' . $rules);
foreach ($secfetch as $rule) {
if (\preg_match_all('/(dest|mode|site)=([^,]+)/', $rule, $matches, PREG_SET_ORDER)) {
$data = ['dest'=>'','mode'=>'','site'=>''];
foreach ($matches as $match) {
$data[$match[1]] = $match[2];
}
if ((!$data['site'] || static::site($data['site']))
&& (!$data['dest'] || !isset($_SERVER['HTTP_SEC_FETCH_DEST']) || static::dest($data['dest']))
&& (!$data['mode'] || !isset($_SERVER['HTTP_SEC_FETCH_MODE']) || static::mode($data['mode']))
) {
return true;
}
}
}
}
}