HTMLSanitizer in TYPO3

Wie man Probleme mit dem neuen Sanitizer beheben kann.

all included

Alexander Böhm
Unser Spezialist für TYPO3 Backend-Berechtigungen, TypoScript und Integrations-Themen.
Lesedauer: ca. 3 Minuten

Mit dem TYPO3 Security-Update vom 10.08.2021 wurde ein HTML Sanitizer in TYPO3 eingeführt (https://typo3.org/article/typo3-core-sa-2021-013). Betroffen sind die Version 7 ELTS, 8 ELTS, 9, 10 und 11.

Das Sicherheitsproblem besteht darin, daß ein Backend-Benutzer über den RTE schädlichen HTML Code einfügen könnte und die bisherigen Mechanismen in TYPO3 nicht ausreichen, um schädlichen Code vollständig zu entfernen.

Die Aufgabe des HTML Sanitizers ist es nun, die HTML Ausgabe von schädlichem Inhalt zu bereinigen und so potentielle Cross-Site Scripting Lücken zu schließen.

Bei unseren Updates bei TYPO3 8, 9 und 10 allerdings wurden an verschiedensten Stellen plötzlich HTML-Attribute entfernt und HTML-Tags escaped. Dazu zählen z.B. unsere mit dem Form-Framwork erstellten Formulare sowie der 'figure'-Tag für Bilder oder das loading-Attribut im 'img'-Tag zum Lazyloading. In diesem Artikel wollen wir deshalb zeigen, wie ihr den Sanitizer entsprechend erweitern oder ausschalten könnt um die Funktionalität der Webseite wieder herzustellen.

Sanitizer global deaktivieren - Möglich, aber keine gute Lösung

Für den Fall, dass der Sanitizer wirklich enorme Probleme macht, habt ihr die Möglichkeit, diesen global zu deaktivieren.
Dazu benötigt ihr im TypoScript nur die folgenden 2 Zeilen:

lib.parseFunc.htmlSanitize = 0
lib.parseFunc_RTE.htmlSanitize = 0

Der Sanitizer ist nun aber Teil der Lösung des Security-Updates. Deshalb sollte ein globales Deaktivieren des Sanitizers nur im Ausnahmefall in Erwägung gezogen werden und auch nicht dauerhaft sein.

Sanitizer punktuell deaktivieren - schon viel besser

Eine bessere Lösung ist es, den Sanitizer nur an bestimmten Stellen zu deaktivieren. Beispiel aus der TYPO3-Doku:

// 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
}

Hierdurch können bewusst bestimmte Bereiche der Seite vom Sanitizer ausgenommen werden, der Schutz der anderen Bereiche bleibt aber weiterhin bestehen. Der ausgenommene Teil sollte dann nur kontrollierbare Inhalte enthalten, die nicht von außerhalb der Seite stammen, z.B. Eingaben über das Frontend.

Achtung: Der Sanitizer wird durch den Aufruf von stdWrap.parseFunc getriggert. 

Dies kann expliziet durch euch z.B. in einem Fluid-Template oder implizit z.B. über die Verwndung des Fluid Viewhelpers 'f:format.html' geschehen. Umwandeln in 'f:format.raw' ist hierfür übrigens meist keine geschickte Lösung, da u.a. Links nicht mehr in das korrekte Format umgewandelt werden.

Sanitizer erweitern - Eigene Tags und Attribute registrieren

Eine weitere Alternative ist die Erweiterung des Sanitizers um zusätzliche Tags und Attribute. 

Achtung: Ihr erweitert dabei eine interne API und solltet euch genau überlegen, welche Attribute und Tags ihr erlaubt!

Hierfür benötigt ihr eine PHP Klasse, die in einer Extension abgelegt werden sollte, z.B. in einer Site-Extension.
In unserem Fall liegt die Klasse unter /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
    {
        // ...
    }
}

Die Methode 'createBehavior()' erlaubt es uns nun weitere Tags und Attribute zum Sanitizer hinzuzufügen.

Im folgenden Beispiel fügen wir das 'figure'-Tag zum Sanitizer hinzu und erlauben für dieses Tag alle allgemeinen (globalen) HTML Attribute. Diese sind bereits im Sanitizer-Package definiert (\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
            )
        );
    }

Mittels 'new Tag()' wird ein neues Tag-Objekt erzeugt. Der erste Parameter ist hier das gewünscht Tag, in unserem Beispiel 'figure'. Im zweiten Parameter werden verschiedene Flags gesetzt, die das Verhalten des Sanitizers für dieses Tag definieren.
Durch den Aufruf der Methode 'addAttrs()' des Tag-Objekts werden außerdem die erlaubten Attribute definiert, in diesem Fall alle globalen HTML-Attribute.

Es können über diesem Weg auch mehrere Tags zum Sanitizer hinzugefügt werden:

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...
            )
        );
    }

Neben den bereits genannten globalen Attributen fügen wir im oberen Fall ein neues Attribut 'value"'zum 'option'-Tag hinzu.

Um bereits definierte Tags um zusätzliche Attribute zu erweitern, könnt ihr euch mit der Methode 'getTag()' das gewünschte Tag aus dem Behaviour holen. In unserem Beispiel fügen wir das Attribut 'loading' zum bestehenden 'img'-Tag hinzu:

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;
}

Zum Schluss muss der eigene Sanitizer noch in der ext_locaconf.php registriert werden. Wir haben in unserem Fall den bestehenden Default-Sanitizer ersetzt:

// 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

Wenn ihr noch nicht genau wisst, was alles auf eurer Seite durch die Nutzung des Sanitizers nicht mehr funktioniert, dann empfehlen wir dafür Logging zu aktivieren. Für weitere Informationen zum Logging besucht die offizielle Dokumentation zum HTMLSanitizer.

In unserer LocalConfiguration.php im TYPO3 9 sieht die Konfiguration so aus (7 entspricht debug log level):


'LOG' => [
    'TYPO3' => [
        'HtmlSanitizer' => [
            'writerConfiguration' => [
                '7' => [
                    TYPO3\CMS\Core\Log\Writer\FileWriter::class => [
                        'logFileInfix' => 'html',
                    ],
                ],
            ],
        ],
    ],
],

Sobald das Logging aktiviert ist, findet ihr auf eurem Server eine Logdatei mit entsprechenden Meldungen des Sanitizers.
Je nachdem, ob die Seite mittels Composer oder klassisch installliert wurde, findet Ihr die Log Datei im Verzeichnis var/log, parallel zum Web-Root Verzeichnis (Composer Modus) bzw. web-root/typo3temp/var/log (klassischer Modus).

Weitere Links

Security Meldung: 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-Eintrag bzgl. fehlender Tags im Sanitizer: https://github.com/TYPO3/html-sanitizer/issues/16#issuecomment-896204718 (Ein genereller Blick auf die offenen Issues lohnt sich inzwischen.)

 

Teilen:

Weitere Beiträge

:(){ :|:& };:
Wolfgang Medina-Erhardt, DevOps bei punkt.de
Arbeiten bei punkt.de