Quelltext des aktuellen Standes
<?php
declare(strict_types=1);
use OpenNIT\Security\XSS;
/**
* Deckt die Eingangsprüfung für href- und src-Attribute ab.
*
* Schwerpunkt ist die Behandlung von data:-URIs: Rasterbilder aus dem Editor
* müssen erhalten bleiben, SVG dagegen ausgeschlossen sein, weil es ein
* XML-Dokument mit Skripten und Dateiverweisen sein kann.
*/
return static function (): void {
test('Rasterbilder als data:-URI bleiben erlaubt', function () {
assertTrue(XSS::isValidUrl('data:image/png;base64,iVBORw0KGgo='));
assertTrue(XSS::isValidUrl('data:image/jpeg;base64,/9j/4AAQ'));
assertTrue(XSS::isValidUrl('data:image/jpg;base64,/9j/4AAQ'));
assertTrue(XSS::isValidUrl('data:image/gif;base64,R0lGODdh'));
assertTrue(XSS::isValidUrl('data:image/webp;base64,UklGRg=='));
assertTrue(XSS::isValidUrl('DATA:IMAGE/PNG;BASE64,iVBORw0KGgo='));
});
test('SVG als data:-URI wird abgewiesen', function () {
assertFalse(XSS::isValidUrl('data:image/svg+xml;base64,PHN2Zz48L3N2Zz4='));
assertFalse(XSS::isValidUrl('data:image/svg+xml;utf8,<svg></svg>'));
assertFalse(XSS::isValidUrl('DATA:IMAGE/SVG+XML;BASE64,PHN2Zz4='));
});
test('verschleierte Medientypen greifen nicht', function () {
// Steuerzeichen und HTML-Entities werden vor dem Abgleich entfernt,
// damit "svg" nicht als Rasterformat durchrutscht.
assertFalse(XSS::isValidUrl("data:image/svg\t+xml;base64,PHN2Zz4="));
assertFalse(XSS::isValidUrl('data:image/svg+xml;base64,PHN2Zz4='));
assertFalse(XSS::isValidUrl("data:\nimage/svg+xml;base64,PHN2Zz4="));
});
test('andere data:-Medientypen bleiben ausgeschlossen', function () {
assertFalse(XSS::isValidUrl('data:text/html;base64,PHNjcmlwdD4='));
assertFalse(XSS::isValidUrl('data:application/xml;base64,PD94bWw+'));
assertFalse(XSS::isValidUrl('data:image/png,ohne-base64'));
});
test('gefährliche Schemata bleiben blockiert', function () {
assertFalse(XSS::isValidUrl('javascript:alert(1)'));
assertFalse(XSS::isValidUrl('vbscript:msgbox(1)'));
assertFalse(XSS::isValidUrl('file:///etc/passwd'));
assertFalse(XSS::isValidUrl(''));
});
test('gewöhnliche Verweise funktionieren weiterhin', function () {
assertTrue(XSS::isValidUrl('/uploads/bild.png'));
assertTrue(XSS::isValidUrl('#abschnitt'));
assertTrue(XSS::isValidUrl('https://example.org/seite'));
assertTrue(XSS::isValidUrl('mailto:info@example.org'));
assertTrue(XSS::isValidUrl('blob:https://example.org/1234'));
assertTrue(XSS::isValidUrl('unterseite.html'));
});
test('ein SVG-Bild überlebt die Bereinigung nicht', function () {
$svg = 'data:image/svg+xml;base64,' . base64_encode('<svg xmlns="http://www.w3.org/2000/svg"/>');
$html = XSS::purifyHtml('<p>Text</p><img src="' . $svg . '" alt="x">');
assertFalse(str_contains($html, 'svg+xml'));
assertTrue(str_contains($html, 'Text'));
});
test('ein Rasterbild überlebt die Bereinigung', function () {
$png = 'data:image/png;base64,iVBORw0KGgo=';
$html = XSS::purifyHtml('<p>Text</p><img src="' . $png . '" alt="x">');
assertTrue(str_contains($html, 'data:image/png;base64,'));
});
test('url() in style-Attributen bleibt ausgeschlossen', function () {
$style = XSS::purifyStyle('background: url(data:image/svg+xml;base64,PHN2Zz4=); color: #333');
assertFalse(str_contains($style, 'url('));
assertTrue(str_contains($style, 'color'));
});
};