Den Sanitizer sicher erweitern
Erweitere oder verschärfe die Erlaubnislisten von Easy SVG, ohne ein Loch zu öffnen: Aufbau, was nie erlaubt sein darf, der alte Global und das Testen.
Tutorial · Easy SVG
Easy SVG bereinigt jedes SVG, bevor WordPress es speichert. Die meisten Sites müssen daran nie etwas ändern. Manchmal exportiert ein Design-Tool aber ein Element, das der Bereiniger nicht kennt, oder eine Site will strengere Regeln. Dann kannst du die Regeln mit drei Filtern anpassen. Dieses Tutorial zeigt, wie der Bereiniger aufgebaut ist, welche Änderungen sicher sind, welche nicht, und wie du beweist, dass deine Änderung tut, was du denkst, bevor sie live geht.
So ist der Sanitizer aufgebaut
Easy SVG nutzt die Bibliothek enshrined/svg-sanitize (mitgeliefert ist Version 1.0.0). Sie arbeitet mit zwei Erlaubnislisten: Elementnamen und Attributnamen. Alles, was nicht auf einer Liste steht, fliegt raus. Zusätzlich entfernt sie immer PHP-Verarbeitungsanweisungen, Kommentare und unsichere href-Werte wie javascript:, und sie macht aus CDATA-Abschnitten einfachen Text.
Jedes Mal, wenn Easy SVG etwas bereinigen muss, ruft es easy_svg_sanitizer() auf. Die Funktion baut einen neuen Sanitizer und wendet in diesem Moment deine Filter an:
Daraus folgen drei Dinge:
- Deine Filter laufen bei jeder Bereinigung, nicht nur einmal. Registriere sie früh (eine Plugin- oder Must-Use-Plugin-Datei reicht), dann gelten sie für jeden Upload in derselben Anfrage und in allen folgenden.
- Jeder Weg sieht dieselben Regeln. Uploads in die Mediathek, Sideloads (
media_sideload_image(), WP-CLI, Importer), die Prüfung inwp_upload_bits()für XML-RPC, die Prüfung jedes neuen Anhangs, die Icon-Bibliothek und Add-ons rufen alle diese eine Funktion auf. - Manche Wege haben eigene feste Schritte. Uploads lehnen vorher Dateien über der Größen- und Komplexitätsgrenze ab und entfernen Animationen, die auf
hrefzielen, falls deine Liste Animationselemente erlaubt. Icons verlieren zusätzlichstyle,script, jedes Animationselement, jedeson*-Attribut und jedeshref, das nicht mit#beginnt. Deine Filter können das nicht abschalten.
Wann die Filter im Verhältnis zum Upload laufen:
Die Upload-Filter gibt es nur, solange SVG-Uploads in der Mediathek erlauben unter Icons, Einstellungen eingeschaltet ist. Die Prüfung neuer Anhänge läuft immer, ein SVG, das ein anderes Plugin hereinlässt, wird also auch bereinigt.
Sichere Änderungen an den Erlaubnislisten
Eine sichere Änderung ergänzt einen Namen, der nur beschreibt, wie etwas aussieht, oder streicht einen, den du nie haben willst. Arbeite mit Belegen, nicht mit Vermutungen.
Herausfinden, was entfernt wird
Schick die Datei durch den Sanitizer und frag, was er herausgenommen hat. getXmlIssues() listet jedes entfernte Element und Attribut mit Zeilennummer.
wp eval '
$s = easy_svg_sanitizer();
$s->sanitize( file_get_contents( "/path/to/export.svg" ) );
foreach ( $s->getXmlIssues() as $i ) { WP_CLI::log( $i["message"] . " (line " . $i["line"] . ")" ); }
'
Eine typische Antwort: Suspicious tag 'feDropShadow' oder Suspicious attribute 'mask-type'.
Entscheiden, ob der Name nur das Aussehen betrifft
Filter-Primitive (fe*), Verläufe, Masken und Darstellungsattribute (mask-type, transform-origin, focusable) ändern nur Pixel. Elemente, die Code ausführen, andere Dokumente einbetten oder andere Attribute mit der Zeit verändern, gehören nicht auf die Liste. Siehe die Tabelle unten.
Die kleinstmögliche Änderung in ein Must-Use-Plugin schreiben
<?php
/**
* Plugin Name: My SVG allow-list
*/
/**
* Keep drop shadows from our design tool.
*
* @param string[] $tags Allowed element names.
* @return string[]
*/
function mysite_svg_tags( $tags ) {
$tags[] = 'feDropShadow';
return $tags;
}
add_filter( 'esw_svg_allowed_tags', 'mysite_svg_tags' );
/**
* Keep the mask type our exports rely on.
*
* @param string[] $attributes Allowed attribute names.
* @return string[]
*/
function mysite_svg_attributes( $attributes ) {
$attributes[] = 'mask-type';
return $attributes;
}
add_filter( 'esw_svg_allowed_attributes', 'mysite_svg_attributes' );
Speichere das als wp-content/mu-plugins/mysite-svg-allow-list.php. Must-Use-Plugins laden bei jeder Anfrage und lassen sich nicht aus Versehen deaktivieren.
Die Prüfungen unten ausführen
Teste, dass deine Datei jetzt behält, was sie braucht, und dass die bekannten bösen Fälle weiterhin entfernt werden.
Wenn du Namen streichst statt ergänzt, vergleiche kleingeschrieben. Die Standardliste mischt Schreibweisen (feBlend, lineargradient), und array_diff() unterscheidet Groß- und Kleinschreibung:
/**
* Never store embedded bitmaps.
*
* @param string[] $tags Allowed element names.
* @return string[]
*/
function mysite_no_bitmaps_in_svg( $tags ) {
return array_values(
array_filter(
$tags,
static function ( $tag ) {
return 'image' !== strtolower( $tag );
}
)
);
}
add_filter( 'esw_svg_allowed_tags', 'mysite_no_bitmaps_in_svg' );
Ein entferntes Element nimmt alles mit, was darin liegt. Streichst du g oder a, kann die ganze Zeichnung leer werden.
Was nie erlaubt sein darf, und warum
| Nie hinzufügen | Warum |
|---|---|
script | Führt JavaScript aus, sobald das SVG direkt geöffnet wird, auch aus der Mediathek heraus. |
on*-Attribute (onload, onclick, ...) | Event-Handler sind ebenfalls JavaScript. Die Attributliste prüft keine Werte, jeder erlaubte Handler läuft also so, wie er geschrieben ist. |
foreignObject | Bettet HTML in die Zeichnung ein. Die SVG-Erlaubnislisten waren nie dafür gedacht, HTML-Inhalte zu beurteilen. |
animate, set | Sie verändern andere Attribute über die Zeit. attributeName="href" mit values="javascript:..." setzt einen Link, den der Sanitizer entfernt hätte. Uploads entfernen dieses eine Muster nachträglich, easy_svg_sanitizer() allein tut das nicht. |
iframe, object, embed | Laden andere Dokumente ins SVG. |
| Neue URL-Attribute | Nur Attributnamen, die href enthalten, bekommen eine Wertprüfung. Ein neues Attribut, das eine URL annimmt, behält javascript:, wie es dasteht. |
Schon auf der Standardliste und dort unbedenklich: style (CSS, fremde Verweise werden entfernt), use, image, a (ihre href-Werte werden geprüft), animateTransform, animateMotion, animateColor.
Deine Admins öffnen hochgeladene SVGs in der Mediathek. Eine Liste, die Skripte durchlässt, macht jedes Konto, das Medien hochladen darf, zu einem Weg, Code im Browser eines Admins auszuführen. In den Einstellungen von Easy SVG lässt sich das Bereinigen nicht abschalten. Diese Filter sind der einzige Weg, deshalb verdient jede Ergänzung einen Grund, den du aufschreiben kannst.
Fremde Verweise
esw_svg_remove_remote_references steht standardmäßig auf true: Jede absolute URL (https://..., //host/...) wird aus Attributen entfernt, und fremde url()-Angaben sowie jedes @import werden aus style-Elementen herausgeschnitten. Deine eigene Domain zählt dabei als fremd, weil die Prüfung die Form der URL ansieht, nicht den Host. Lass das eingeschaltet, solange du nicht jedem vertraust, der hochladen darf. Brauchst du fremde Verweise nur für einen Import, hänge vorher __return_false ein und nimm es danach wieder heraus. Der Wert wird bei jedem Aufbau eines Sanitizers neu gelesen.
Der alte globale $sanitizer
Easy SVG 4.1 hat Uploads mit einem einzigen Sanitizer in der globalen Variable $sanitizer bereinigt, und manche Sites haben ihn per Snippet eingestellt:
global $sanitizer;
$sanitizer->removeXMLTag( true );
Easy SVG legt diese globale Variable beim Laden weiterhin an, solange die Bibliothek da ist und der Name nicht anderweitig belegt ist. Sie ist jetzt eine Vorlage: easy_svg_sanitizer() klont sie, Einstellungen daran gehen also auf jede Kopie über. In der Praxis heißt das:
- Funktioniert weiterhin: Ausgabe-Einstellungen wie
removeXMLTag( true )undminify( true ). Die Testsuite beweist, dass einremoveXMLTag()am globalen Objekt den gespeicherten Upload erreicht. - Wirkt nicht:
setAllowedTags(),setAllowedAttrs()undremoveRemoteReferences(). Die Kopie bekommt nach dem Klonen die gefilterten Listen und die gefilterte Einstellung für fremde Verweise, die gewinnen also immer. Verschieb diese Einstellungen in die drei Filter. - Wird nie durch Benutzung verändert: Easy SVG ändert das globale Objekt selbst nie und gibt es auch nie heraus.
- Fremdes wird nicht aufgerufen: Hat ein anderes Plugin ein ganz anderes Objekt in
$sanitizergelegt, ignoriert Easy SVG es und baut einen frischen Sanitizer.
Neuer Code sollte das globale Objekt gar nicht anfassen. Nimm die Filter für die Regeln und easy_svg_sanitizer() für einen eigenen Sanitizer.
Deine Änderungen testen
Bekannt gute und bekannt böse Dateien durch den Sanitizer schicken
Speichere das als svg-sanitizer-check.php und führe wp eval-file svg-sanitizer-check.php auf einer Staging-Kopie der Site aus. Es nutzt die echten Filter der Site, weil es easy_svg_sanitizer() fragt.
@@CHECKSCRIPT@@
Ergänze für jeden Namen, den du erlaubt hast, einen Fall (er muss bleiben), und lass die bösen Fälle unverändert. Hat die Site animate auf ihre Liste gesetzt, schlägt der Fall href animation hier fehl: Dieses Muster wird nur auf dem Upload-Weg entfernt, und den prüfst du im übernächsten Schritt.
Beweisen, dass der Test fehlschlagen kann
Bau die verbotene Änderung kurz über ein Wegwerf-Must-Use-Plugin ein, führ das Skript erneut aus und sieh zu, wie es fehlschlägt. Im WordPress-Hauptverzeichnis deiner Staging-Site:
printf '%s
' '<?php' 'add_filter( "esw_svg_allowed_attributes", function ( $a ) { $a[] = "onload"; return $a; } );' > wp-content/mu-plugins/zz-svg-mutation.php
wp eval-file svg-sanitizer-check.php
rm wp-content/mu-plugins/zz-svg-mutation.php
Der Fall event handler muss jetzt FAIL event handler: kept onload melden, und der Befehl muss mit einem Fehler enden. Ein Test, der nicht fehlschlagen kann, beweist nichts. Vergiss das rm nicht: Solange die Datei existiert, nimmt die Site onload tatsächlich an.
Den echten Upload-Weg prüfen
Schalte SVG-Uploads in der Mediathek erlauben ein und importiere über WP-CLI eine böse und eine kaputte Datei. WP-CLI nutzt denselben Sideload-Weg wie Importer:
printf '%s' '<svg xmlns="http://www.w3.org/2000/svg"><script>alert(1)</script><rect width="9" height="9"/></svg>' > /tmp/evil.svg
printf '%s' '<svg><rect></svg' > /tmp/broken.svg
ID=$(wp media import /tmp/evil.svg --porcelain)
grep -c '<script' "$(wp eval "echo get_attached_file( $ID );")"
wp media import /tmp/broken.svg
Der erste Import klappt, und grep gibt 0 aus: Gespeichert ist die bereinigte Datei. Der zweite Import wird mit der Begründung „Bitte überprüfe deine SVG-Datei.“ abgelehnt (auf einer englischen Site „Sorry, please check your SVG file.“). Lösch den Test-Anhang danach mit wp post delete $ID --force.
Editor und Mediathek mit eigenen Augen prüfen
Lade einen deiner echten Exporte unter Medien, Mediendatei hinzufügen hoch, öffne ihn in der Mediathek und setz ihn in einen Beitrag. Sieht etwas anders aus als im Export, geh zurück zum ersten Schritt im Abschnitt davor und sieh dir getXmlIssues() an.
Checkliste
- Jeder Name, den du ergänzt hast, ändert nur, wie die Zeichnung aussieht.
- Beim Streichen vergleichst du kleingeschrieben, und deine Callbacks geben immer ein Array zurück.
- Das Prüfskript läuft grün und schlägt fehl, wenn du absichtlich
onloadergänzt. wp media importspeichert eine bereinigte Datei und lehnt eine kaputte ab.- Dateien, die schon in der Mediathek liegen, wurden mit den alten Regeln bereinigt. Lade die wichtigen neu hoch, wenn deine Änderung sie betrifft.