Content Security Policy¶
New in version 12.3.
Introduction¶
Content Security Policy (CSP) is a security standard introduced to prevent cross-site scripting (XSS), clickjacking and other code injection attacks resulting of malicious content being executed in the trusted web page context.
See also
If you are not familiar with Content Security Policy, please read the following resources:
Content Security Policy declarations can be applied to a TYPO3 website in frontend and backend scope with a dedicated API.
To delegate Content Security Policy handling to TYPO3, the scope-specific feature flags need to be enabled:
$GLOBALS['TYPO3_CONF_VARS']['SYS']['features']['security.backend.enforceContentSecurityPolicy']
$GLOBALS['TYPO3_CONF_VARS']['SYS']['features']['security.frontend.enforceContentSecurityPolicy']
For new installations security.backend.enforceContentSecurityPolicy
is
enabled by default.
Configuration¶
Policy builder approach¶
The following approach illustrates how a policy is build:
<?php
use TYPO3\CMS\Core\Security\ContentSecurityPolicy\Directive;
use TYPO3\CMS\Core\Security\ContentSecurityPolicy\Policy;
use TYPO3\CMS\Core\Security\ContentSecurityPolicy\SourceKeyword;
use TYPO3\CMS\Core\Security\ContentSecurityPolicy\SourceScheme;
use TYPO3\CMS\Core\Security\ContentSecurityPolicy\UriValue;
use TYPO3\CMS\Core\Security\Nonce;
$nonce = Nonce::create();
$policy = (new Policy())
// Results in `default-src 'self'`
->default(SourceKeyword::self)
// Extends the ancestor directive ('default-src'),
// thus reuses 'self' and adds additional sources
// Results in `img-src 'self' data: https://*.typo3.org`
->extend(Directive::ImgSrc, SourceScheme::data, new UriValue('https://*.typo3.org'))
// Extends the ancestor directive ('default-src'),
// thus reuses 'self' and adds additional sources
// Results in `script-src 'self' 'nonce-[random]'`
// ('nonce-proxy' is substituted when compiling the policy)
->extend(Directive::ScriptSrc, SourceKeyword::nonceProxy)
// Sets (overrides) the directive,
// thus ignores 'self' of the 'default-src' directive
// Results in `worker-src blob:`
->set(Directive::WorkerSrc, SourceScheme::blob);
header('Content-Security-Policy: ' . $policy->compile($nonce));
The result of the compiled and serialized result as HTTP header would look similar to this (the following sections are using the same example, but utilize different techniques for the declarations):
Content-Security-Policy: default-src 'self';
img-src 'self' data: https://*.typo3.org; script-src 'self' 'nonce-[random]';
worker-src blob:
Extension-specific¶
Policies for frontend and backend can be applied automatically by providing a
Configuration/ContentSecurityPolicies.php
file in an extension, for
example:
<?php
declare(strict_types=1);
use TYPO3\CMS\Core\Security\ContentSecurityPolicy\Directive;
use TYPO3\CMS\Core\Security\ContentSecurityPolicy\Mutation;
use TYPO3\CMS\Core\Security\ContentSecurityPolicy\MutationCollection;
use TYPO3\CMS\Core\Security\ContentSecurityPolicy\MutationMode;
use TYPO3\CMS\Core\Security\ContentSecurityPolicy\Scope;
use TYPO3\CMS\Core\Security\ContentSecurityPolicy\SourceKeyword;
use TYPO3\CMS\Core\Security\ContentSecurityPolicy\SourceScheme;
use TYPO3\CMS\Core\Security\ContentSecurityPolicy\UriValue;
use TYPO3\CMS\Core\Type\Map;
return Map::fromEntries([
// Provide declarations for the backend
Scope::backend(),
// NOTICE: When using `MutationMode::Set` existing declarations will be overridden
new MutationCollection(
// Results in `default-src 'self'`
new Mutation(
MutationMode::Set,
Directive::DefaultSrc,
SourceKeyword::self
),
// Extends the ancestor directive ('default-src'),
// thus reuses 'self' and adds additional sources
// Results in `img-src 'self' data: https://*.typo3.org`
new Mutation(
MutationMode::Extend,
Directive::ImgSrc,
SourceScheme::data,
new UriValue('https://*.typo3.org')
),
// Extends the ancestor directive ('default-src'),
// thus reuses 'self' and adds additional sources
// Results in `script-src 'self' 'nonce-[random]'`
// ('nonce-proxy' is substituted when compiling the policy)
new Mutation(
MutationMode::Extend,
Directive::ScriptSrc,
SourceKeyword::nonceProxy
),
// Sets (overrides) the directive,
// thus ignores 'self' of the 'default-src' directive
// Results in `worker-src blob:`
new Mutation(
MutationMode::Set,
Directive::WorkerSrc,
SourceScheme::blob
),
),
]);
Site-specific (frontend)¶
In frontend, a dedicated sites/<my_site>/csp.yaml
can be
used to declare policies for a specific site, for example:
# Inherits default site-unspecific frontend policy mutations (enabled per default)
inheritDefault: true
mutations:
# Results in `default-src 'self'`
- mode: set
directive: 'default-src'
sources:
- "'self'"
# Extends the ancestor directive ('default-src'),
# thus reuses 'self' and adds additional sources
# Results in `img-src 'self' data: https://*.typo3.org`
- mode: extend
directive: 'img-src'
sources:
- 'data:'
- 'https://*.typo3.org'
# Extends the ancestor directive ('default-src'),
# thus reuses 'self' and adds additional sources
# Results in `script-src 'self' 'nonce-[random]'`
# ('nonce-proxy' is substituted when compiling the policy)
- mode: extend
directive: 'script-src'
sources:
- "'nonce-proxy'"
# Results in `worker-src blob:`
- mode: set
directive: 'worker-src'
sources:
- 'blob:'
Reporting of violations¶
Potential CSP violations are reported back to the TYPO3 system and persisted
internally in the database table sys_http_report
. A corresponding
Admin Tools > Content Security Policy backend module supports users
to keep track of recent violations and - if applicable - to select potential
resolutions (stored in the database table sys_csp_resolution
) which
extends the Content Security Policy for the given scope during runtime:

Backend module "Content Security Policy" which displays the violations¶
Clicking on a row displays the details of this violation on the right side including suggestions on how to resolve this violation. You have the choice to apply this suggestion, or to mute or delete the specific violation.
Note
If you apply the suggestion, it is stored in the database table
sys_csp_resolution
. To have all policies in one place, you
should consider adding the suggestion to your
extension-specific or
site-specific CSP definitions
manually.
Using a third-party service¶
As an alternative, the reporting URL can be configured to use a third-party service as well:
// For backend
$GLOBALS['TYPO3_CONF_VARS']['BE']['contentSecurityPolicyReportingUrl']
= 'https://csp-violation.example.org/';
// For frontend
$GLOBALS['TYPO3_CONF_VARS']['FE']['contentSecurityPolicyReportingUrl']
= 'https://csp-violation.example.org/';
Violations are then sent to the third-party service instead of the TYPO3 endpoint.