Important: #107062 - Avoid applying Content-Security-Policy nonce sources when not required
See forge#107062
Description
Using nonce sources in a Content-Security-Policy (CSP) HTTP header implicitly leads
to having a Cache- HTTP response header and internally
requires to renew the nonce value that is present in cached HTML contents, which
has a negative impact on performance.
This change aims for having fully cached pages and tries to avoid nonce sources in the CSP header when actually feasible.
- The
Consumableclass was refactored – it no longer extendsNonce Consumable.String - Two new counters are introduced:
consume– the nonce is required for an inline resource.Inline () consume– the nonce is optional for a static resource.Static ()
Example usage:
<?php
$nonce = new ConsumableNonce();
$nonce->consumeInline(Directive::ScriptSrcElem); // inline script
$nonce->consumeStatic(Directive::StyleSrcElem); // static style
Nonce sources are removed from the CSP policy in the following
situations, in case the request is supposed to be fully cacheable
(config. and not having any USER_ or COA_ items):
- The response body is readable and contains no bytes.
- The nonce consumption counter for all usages equals zero.
- A directive contains a source‑keyword exception (e.g.
'unsafe-) that makes a nonce unnecessary.inline' - The
Policyhas been dispatched and explicitly tells the policy to avoid using nonce sources.Prepared Event
When the nonce should be removed, both the frontend and backend
Content middleware strip the nonce-related
literals from the rendered HTML.
New PSR-14 event
<?php
declare(strict_types=1);
namespace Example\MyPackage\EventListener;
use TYPO3\CMS\Core\Attribute\AsEventListener;
use TYPO3\CMS\Core\Security\ContentSecurityPolicy\Disposition;
use TYPO3\CMS\Core\Security\ContentSecurityPolicy\Event\PolicyPreparedEvent;
#[AsEventListener('my-package/content-security-policy/avoid-nonce')]
final class DropNonceEventListener
{
public function __invoke(PolicyPreparedEvent $event): void
{
$policyBag = $event->policyBag;
if (
isset($policyBag->dispositionMap[Disposition::enforce])
&& $policyBag->scope->siteIdentifier === 'my-special-site'
// YOLO: drop nonce sources, even it is consumed
&& $policyBag->nonce->count() > 0
) {
$policyBag->behavior->useNonce = false;
}
}
}
New
useNonce property
The
\TYPO3\
class now contains the nullable boolean property
use:
true- explicitly allows using nonce sourcesnull- the default unspecific state (the system will detect and decide automatically)false- explicitly denies using nonce sources, it also drops constraints like'strict-since that source keyword requires a nonce sourcedynamic'