With the TYPO3 Security Update of 10.08.2021 an HTML Sanitizer was introduced in TYPO3(https://typo3.org/article/typo3-core-sa-2021-013). The versions affected are 7 ELTS, 8 ELTS, 9, 10 and 11.
The security problem is that a backend user could insert malicious HTML code via the RTE and the existing mechanisms in TYPO3 are not sufficient to completely remove malicious code.
The task of the HTML Sanitizer is now to clean the HTML output from malicious content and thus close potential cross-site scripting gaps.
In our updates to TYPO3 8, 9 and 10, however, HTML attributes were suddenly removed and HTML tags escaped in various places. These include, for example, our forms created with the form framework as well as the 'figure' tag for images or the loading attribute in the 'img' tag for lazyloading. In this article, we therefore want to show you how you can extend or deactivate the sanitizer accordingly to restore the functionality of the website.
Disable sanitizer globally - possible, but not a good solution
In the event that the sanitizer really causes enormous problems, you have the option of deactivating it globally.
To do this, you only need the following 2 lines in the TypoScript:
lib.parseFunc.htmlSanitize = 0
lib.parseFunc_RTE.htmlSanitize = 0
However, the sanitizer is now part of the security update solution. Therefore, a global deactivation of the sanitizer should only be considered in exceptional cases and should not be permanent.
Disabling the sanitizer selectively - much better
A better solution is to deactivate the sanitizer only at certain points. Example from the TYPO3 documentation:
// or disable individually per use-case
10 = TEXT
10 {
value = <div><img src="invalid.file" onerror="alert(1)"></div>
parseFunc =< lib.parseFunc_RTE
parseFunc.htmlSanitize = 0
}
This allows certain areas of the page to be deliberately excluded from the sanitizer, while the protection of other areas remains in place. The excluded part should then only contain controllable content that does not originate from outside the page, e.g. input via the frontend.
Attention: The sanitizer is triggered by calling stdWrap.parseFunc.
This can be done explicitly by you, e.g. in a Fluid template, or implicitly, e.g. by using the Fluid view helper 'f:format.html'. Converting to 'f:format.raw' is usually not a good solution, as links are no longer converted to the correct format.
Extend Sanitizer - register your own tags and attributes
Another alternative is to add additional tags and attributes to the sanitizer.
Attention: You are extending an internal API and should think carefully about which attributes and tags you allow!
To do this, you need a PHP class that should be stored in an extension, e.g. in a site extension.
In our case, the class is located under /Classes/Sanitizer/CustomHtmlSanitizer.php:
<?php
declare(strict_types=1);
namespace PunktDe\PtSite\Sanitizer;
use TYPO3\CMS\Core\Html\DefaultSanitizerBuilder;
use TYPO3\HtmlSanitizer\Behavior;
use TYPO3\HtmlSanitizer\Behavior\Attr;
use TYPO3\HtmlSanitizer\Behavior\Tag;
class CustomHtmlSanitizer extends DefaultSanitizerBuilder
{
public function createBehavior(): Behavior
{
// ...
}
}
The 'createBehavior()' method now allows us to add further tags and attributes to the sanitizer.
In the following example, we add the 'figure' tag to the sanitizer and allow all general (global) HTML attributes for this tag. These are already defined in the sanitizer package (\TYPO3\HtmlSanitizer\Builder\CommonBuilder).
public function createBehavior(): Behavior
{
// extends existing behavior, adds new tag
return parent::createBehavior()
->withName('common')
->withTags(
(new Tag(
'figure',
Tag::ALLOW_CHILDREN + Behavior::ENCODE_INVALID_TAG + Behavior::REMOVE_UNEXPECTED_CHILDREN
))
->addAttrs(
...$this->globalAttrs
)
);
}
A new tag object is created using 'new Tag()'. The first parameter here is the desired tag, in our example 'figure'. In the second parameter, various flags are set that define the behaviour of the sanitizer for this tag.
By calling the 'addAttrs()' method of the tag object, the permitted attributes are also defined, in this case all global HTML attributes.
Several tags can also be added to the sanitizer in this way:
public function createBehavior(): Behavior
{
return parent::createBehavior()
->withName('common')
->withTags(
(new Tag(
'select',
Tag::ALLOW_CHILDREN
))->addAttrs(
...$this->globalAttrs
),
(new Tag(
'option',
Tag::ALLOW_CHILDREN
))->addAttrs(
(new Attr('value')),
...$this->globalAttrs
),
// more tags...
)
);
}
In addition to the global attributes already mentioned, we add a new 'value' attribute to the 'option' tag in the case above.
To add additional attributes to already defined tags, you can use the 'getTag()' method to get the desired tag from the behavior. In our example, we add the 'loading' attribute to the existing 'img' tag:
public function createBehavior(): Behavior
{
$behaviour = parent::createBehavior();
// add loading attribute to img tag
$imgTag = $behaviour->getTag('img');
if ($imgTag !== null) {
$imgTag->addAttrs(new Attr('loading'));
}
return $behaviour;
}
Finally, the custom sanitizer must be registered in ext_locaconf.php. In our case, we have replaced the existing default sanitizer:
// Override HTML sanitizer to allow tags and attributes that we need
$GLOBALS['TYPO3_CONF_VARS']['SYS']['htmlSanitizer']['default'] = PunktDe\PtSite\Sanitizer\CustomHtmlSanitizer::class;
Sanitizer logging
If you do not yet know exactly what has stopped working on your site as a result of using the Sanitizer, we recommend activating logging. For more information on logging, visit the official HTMLSanitizer documentation.
In our LocalConfiguration.php in TYPO3 9 the configuration looks like this (7 corresponds to debug log level):
'LOG' => [
'TYPO3' => [
'HtmlSanitizer' => [
'writerConfiguration' => [
'7' => [
TYPO3\CMS\Core\Log\Writer\FileWriter::class => [
'logFileInfix' => 'html',
],
],
],
],
],
],
As soon as logging is activated, you will find a log file on your server with corresponding messages from the Sanitizer.
Depending on whether the site was installed using Composer or classic mode, you will find the log file in the var/log directory, parallel to the web root directory (Composer mode) or web-root/typo3temp/var/log (classic mode).
Further links
Security message: https://typo3.org/article/typo3-core-sa-2021-013
Changelog: https://docs.typo3.org/c/typo3/cms-core/master/en-us/Changelog/9.5.x/Important-94484-IntroduceHTMLSanitizer.html
Bug entry regarding missing tags in the sanitizer: https://github.com/TYPO3/html-sanitizer/issues/16#issuecomment-896204718 (A general look at the open issues is worthwhile in the meantime)