OpenNIT

Quelltext des aktuellen Standes

Fassung 1.1.1

XssTest.php

4 KB
<?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&#43;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'));
    });
};