Make CSP header code more readable

Signed-off-by: Maximilian Krög <maxi_kroeg@web.de>
This commit is contained in:
Maximilian Krög 2026-03-07 23:47:26 +01:00
parent 2522b3610e
commit 8f15b3f4a4
No known key found for this signature in database
GPG Key ID: 3C00897BB53AAB9C
2 changed files with 77 additions and 82 deletions

View File

@ -22,9 +22,9 @@ use Psr\Clock\ClockInterface;
use function array_merge;
use function htmlspecialchars;
use function implode;
use function ini_get;
use function json_encode;
use function sprintf;
use const JSON_HEX_TAG;
@ -332,7 +332,49 @@ class Header
/** @return array<string, string> */
public function getHttpHeaders(ClockInterface|null $clock = null): array
{
$headers = [];
$headers = [
'Referrer-Policy' => 'same-origin',
'Content-Security-Policy' => $this->getCspHeader(),
/**
* Re-enable possible disabled XSS filters.
*
* @see https://developer.mozilla.org/docs/Web/HTTP/Headers/X-XSS-Protection
*/
'X-XSS-Protection' => '1; mode=block',
/**
* "nosniff", prevents Internet Explorer and Google Chrome from MIME-sniffing
* a response away from the declared content-type.
*
* @see https://developer.mozilla.org/docs/Web/HTTP/Headers/X-Content-Type-Options
*/
'X-Content-Type-Options' => 'nosniff',
/**
* Adobe cross-domain-policies.
*
* @see https://www.sentrium.co.uk/labs/application-security-101-http-headers
*/
'X-Permitted-Cross-Domain-Policies' => 'none',
/**
* Robots meta tag.
*
* @see https://developers.google.com/search/docs/crawling-indexing/robots-meta-tag
*/
'X-Robots-Tag' => 'noindex, nofollow',
/**
* The HTTP Permissions-Policy header provides a mechanism to allow and deny
* the use of browser features in a document
* or within any <iframe> elements in the document.
*
* @see https://developer.mozilla.org/docs/Web/HTTP/Headers/Permissions-Policy
*/
'Permissions-Policy' => 'fullscreen=(self), interest-cohort=()',
];
/* Prevent against ClickJacking by disabling framing */
if ($this->config->config->AllowThirdPartyFraming === 'sameorigin') {
@ -341,48 +383,6 @@ class Header
$headers['X-Frame-Options'] = 'DENY';
}
$headers['Referrer-Policy'] = 'same-origin';
$headers['Content-Security-Policy'] = $this->getCspHeader();
/**
* Re-enable possible disabled XSS filters.
*
* @see https://developer.mozilla.org/docs/Web/HTTP/Headers/X-XSS-Protection
*/
$headers['X-XSS-Protection'] = '1; mode=block';
/**
* "nosniff", prevents Internet Explorer and Google Chrome from MIME-sniffing
* a response away from the declared content-type.
*
* @see https://developer.mozilla.org/docs/Web/HTTP/Headers/X-Content-Type-Options
*/
$headers['X-Content-Type-Options'] = 'nosniff';
/**
* Adobe cross-domain-policies.
*
* @see https://www.sentrium.co.uk/labs/application-security-101-http-headers
*/
$headers['X-Permitted-Cross-Domain-Policies'] = 'none';
/**
* Robots meta tag.
*
* @see https://developers.google.com/search/docs/crawling-indexing/robots-meta-tag
*/
$headers['X-Robots-Tag'] = 'noindex, nofollow';
/**
* The HTTP Permissions-Policy header provides a mechanism to allow and deny
* the use of browser features in a document
* or within any <iframe> elements in the document.
*
* @see https://developer.mozilla.org/docs/Web/HTTP/Headers/Permissions-Policy
*/
$headers['Permissions-Policy'] = 'fullscreen=(self), interest-cohort=()';
$headers = array_merge($headers, Core::getNoCacheHeaders($clock ?? new Clock()));
/**
@ -427,32 +427,25 @@ class Header
private function getCspHeader(): string
{
$mapTileUrl = ' tile.openstreetmap.org';
$captchaUrl = '';
$cspAllow = $this->config->config->CSPAllow;
$cspAllow = $this->config->config->CSPAllow === '' ? '' : ' ' . $this->config->config->CSPAllow;
$captchaUrl =
$this->config->config->CaptchaLoginPrivateKey === '' ||
$this->config->config->CaptchaLoginPublicKey === '' ||
$this->config->config->CaptchaApi === '' ||
$this->config->config->CaptchaRequestParam === '' ||
$this->config->config->CaptchaResponseParam === ''
? ''
: ' ' . $this->config->config->CaptchaCsp;
if (
$this->config->config->CaptchaLoginPrivateKey !== ''
&& $this->config->config->CaptchaLoginPublicKey !== ''
&& $this->config->config->CaptchaApi !== ''
&& $this->config->config->CaptchaRequestParam !== ''
&& $this->config->config->CaptchaResponseParam !== ''
) {
$captchaUrl = ' ' . $this->config->config->CaptchaCsp . ' ';
}
$csp = [
"default-src 'self'" . $captchaUrl . $cspAllow,
"img-src 'self' data:" . $captchaUrl . $cspAllow . $mapTileUrl,
"object-src 'none'",
"script-src 'self' 'unsafe-inline' 'unsafe-eval'" . $captchaUrl . $cspAllow,
"style-src 'self' 'unsafe-inline'" . $captchaUrl . $cspAllow,
];
return sprintf(
'default-src \'self\' %s%s;script-src \'self\' \'unsafe-inline\' \'unsafe-eval\' %s%s;'
. 'style-src \'self\' \'unsafe-inline\' %s%s;img-src \'self\' data: %s%s%s;object-src \'none\';',
$captchaUrl,
$cspAllow,
$captchaUrl,
$cspAllow,
$captchaUrl,
$cspAllow,
$cspAllow,
$mapTileUrl,
$captchaUrl,
);
return implode('; ', $csp) . ';';
}
private function getVariablesForJavaScript(): string

View File

@ -199,7 +199,6 @@ class HeaderTest extends AbstractTestCase
$config->set('CaptchaCsp', $captchaCsp);
$expected = [
'X-Frame-Options' => $expectedFrameOptions ?? '',
'Referrer-Policy' => 'same-origin',
'Content-Security-Policy' => $expectedCsp,
'X-XSS-Protection' => '1; mode=block',
@ -207,6 +206,7 @@ class HeaderTest extends AbstractTestCase
'X-Permitted-Cross-Domain-Policies' => 'none',
'X-Robots-Tag' => 'noindex, nofollow',
'Permissions-Policy' => 'fullscreen=(self), interest-cohort=()',
'X-Frame-Options' => $expectedFrameOptions ?? '',
'Expires' => 'Wed, 21 Oct 2015 07:28:00 GMT',
'Cache-Control' => 'no-store, no-cache, must-revalidate, pre-check=0, post-check=0, max-age=0',
'Pragma' => 'no-cache',
@ -231,9 +231,11 @@ class HeaderTest extends AbstractTestCase
'',
'',
'DENY',
'default-src \'self\' ;script-src \'self\' \'unsafe-inline\' \'unsafe-eval\' ;'
. 'style-src \'self\' \'unsafe-inline\' ;img-src \'self\' data: tile.openstreetmap.org;'
. 'object-src \'none\';',
"default-src 'self';"
. " img-src 'self' data: tile.openstreetmap.org;"
. " object-src 'none';"
. " script-src 'self' 'unsafe-inline' 'unsafe-eval';"
. " style-src 'self' 'unsafe-inline';"
],
[
'sameorigin',
@ -242,12 +244,11 @@ class HeaderTest extends AbstractTestCase
'PublicKey',
'captcha.tld csp.tld',
'SAMEORIGIN',
'default-src \'self\' captcha.tld csp.tld example.com example.net;'
. 'script-src \'self\' \'unsafe-inline\' \'unsafe-eval\' '
. 'captcha.tld csp.tld example.com example.net;'
. 'style-src \'self\' \'unsafe-inline\' captcha.tld csp.tld example.com example.net;'
. 'img-src \'self\' data: example.com example.net tile.openstreetmap.org captcha.tld csp.tld ;'
. 'object-src \'none\';',
"default-src 'self' captcha.tld csp.tld example.com example.net;"
. " img-src 'self' data: captcha.tld csp.tld example.com example.net tile.openstreetmap.org;"
. " object-src 'none';"
. " script-src 'self' 'unsafe-inline' 'unsafe-eval' captcha.tld csp.tld example.com example.net;"
. " style-src 'self' 'unsafe-inline' captcha.tld csp.tld example.com example.net;"
],
[
true,
@ -256,10 +257,11 @@ class HeaderTest extends AbstractTestCase
'PublicKey',
'captcha.tld csp.tld',
null,
'default-src \'self\' captcha.tld csp.tld ;'
. 'script-src \'self\' \'unsafe-inline\' \'unsafe-eval\' captcha.tld csp.tld ;'
. 'style-src \'self\' \'unsafe-inline\' captcha.tld csp.tld ;'
. 'img-src \'self\' data: tile.openstreetmap.org captcha.tld csp.tld ;object-src \'none\';',
"default-src 'self' captcha.tld csp.tld;"
. " img-src 'self' data: captcha.tld csp.tld tile.openstreetmap.org;"
. " object-src 'none';"
. " script-src 'self' 'unsafe-inline' 'unsafe-eval' captcha.tld csp.tld;"
. " style-src 'self' 'unsafe-inline' captcha.tld csp.tld;",
],
];
}