Skip to content

Cross-Site Scripting verhindern, ganz allgemein

Am einfachsten verhindern Sie Cross-Site Scripting, indem Sie gar kein HTML und damit JavaScript als Benutzereingabe akzeptieren. Wenn die Eingabe nur aus reinem Klartext bestehen darf, kann sie beim Finden des ersten HTML-Tags verworfen werden. Denn dann versucht jemand einen Angriff, und der wird sofort abgewehrt, fertig, aus. Sie sollten nicht mal den Versuch machen, den enthaltenen Code aus der Eingabe zu löschen. Denn die Eingabe dient einzig und allein dazu, Schadcode in die Seiten Ihrer Webanwendung einzuschleusen. Es gibt darin nichts, was aus irgend einem Grund gerettet werden müsste.

Diese ebenso einfache wie effektive Lösung ist heutzutage nicht mehr immer möglich. In Zeiten des Web 2.0 mit seinem "User generated content" ist HTML-Code, zum Beispiel Auszeichnungs- oder Formatierungsanweisungen, oft ein erwünschter Teil der Eingabe. Und damit wird es deutlich schwieriger, erwünschte und unerwünschte Inhalte auseinander zu halten.

Ausgezeichnet, ganz ohne HTML!

Relativ einfach wird das Ganze, wenn statt HTML eine Alternative wie BBCode verwendet werden kann. Für fett gedruckten Text wird dann statt der HTML-Anweisung <b>fett</b> die BBCode-Anweisung [b]fett[/b] verwendet, < und > werden nicht mehr benötigt und können gelöscht oder umgewandelt werden.

Werden alle < und > durch ihre HTML-Entities &lt; und &gt; ersetzt, können keine HTML-Tags wie zum Beispiel <script>..</script> mehr eingeschleust werden. Wie Sie aber bereits gesehen haben, sind in bestimmten Fällen auch ohne diese Zeichen Angriffe möglich.

Daher müssen bei der Verwendung von BBCode alle gefundenen HTML-Tags gelöscht oder die Eingabe beim Finden eines HTML-Tags verworfen werden. Wieso, steht bereits oben: Sie werden sehr wahrscheinlich gerade angegriffen, und warum sollten Sie dem Angreifer auch noch helfen?

Um aber Bedienungsfehler nicht zu hart zu bestrafen, könnten Sie die Eingabe auch erst mal nur zurückweisen und dem Benutzer die Möglichkeit geben, versehentlich eingegebene HTML-Tags durch ihre BBCode-Entsprechungen zu ersetzen. Dabei gibt es nur ein kleines Problem: Sie müssen aufpassen, dass ein Angreifer dieses Zurückweisen nicht für einen Angriff auf Ihre Benutzer ausnutzen kann. Ist es möglich, einem Benutzer eine präparierte Eingabe unter zu schieben, die der dann an die Webanwendung schickt, würde durch das Zurückweisen der eingeschleuste XSS-Code im Browser des Benutzers landen. Und so was nennt man bekanntlich Reflektiertes XSS. Sie könnten versuchen, den möglicherweise eingeschleusten XSS-Code zu entschärfen, so dass er nicht ausgeführt werden kann, aber das ist gar nicht so einfach, wie Sie noch sehen werden.

Falls Sie die Eingabe trotz aller Bedenken an den Benutzer zurück geben wollen, übertreiben Sie es dabei aber nicht, ein oder zwei zusätzliche Versuche sind genug. Mehr würden einem Angreifer nur ermutigen und womöglich dabei helfen, einen Fehler in der Schutzfunktion zu ermitteln und auszunutzen.

Und um noch mal auf den BBCode zurück zu kommen: Der dient ja nur zur Eingabe des Texts. Für die Ausgabe muss der BBCode in HTML-Code umgewandelt werden. Dabei müssen Sie natürlich auch darauf achten, dass in der Ausgabe kein Schadcode landet. Nicht, dass am Ende geschickt formulierter BBCode von der Webanwendung in ausführbaren JavaScript-Code umgewandelt wird.

HTML mit Blacklist - Das kann nicht gut gehen!

Ein weiterer Ansatz zum Prüfen und Filtern der Eingaben ist das Ausfiltern unerwünschter Tags anhand von Negativlisten, den Blacklists. Damit wirklich kein Schadcode eingeschleust werden kann, müssen dabei alle möglicherweise gefährlichen Tags und alle möglichen Tarnungen dieser Tags erfasst werden. Naturgemäß kann so eine Liste nie vollständig sein. Wenn man denkt, alle möglichen Tags und deren Variationen und Kombinationen gefunden zu haben, findet bestimmt irgend jemand eine neue Methode, funktionsfähigen Code an der Blacklist vorbei zu schleusen.

Nachlässig implementierte Schutzfunktionen lassen sich zum Beispiel so umgehen:

  • Funktionen, die nur in <...> eingeschlossene Tags erkennen, können durch unvollständige Tags umgangen werden, die in manchen Browsern trotz ihres Fehlers interpretiert werden, zum Beispiel
    <img src="" onerror=alert(xss!)
  • Funktionen, die ein Nullbyte als Zeilenende interpretieren und darauf folgenden Text ignorieren, können durch ein URL-kodiertes Nullbyte vor dem Schadcode getäuscht werden:
    harmlos%00<script>alert('XSS')</script>
  • Wie schon erwähnt kann der Angriff auch über eine andere als die erwartete Übertragungsmethode erfolgen. Es muss also darauf geachtet werden, das der richtige Parameter geprüft wird, nämlich der, der hinterher auch verwendet wird.

Wie bereits mehrmals erwähnt enthalten das XSS Filter Evasion Cheat Sheet von OWASP und das HTML5 Security Cheatsheet eine umfangreiche Sammlung von Möglichkeiten, wo und wie XSS-Code eingeschleust werden kann. Damit lassen sich viele Filter umgehen. Falls Sie noch eine Blacklist verwenden, machen Sie doch mal einen Test mit den Cheat Sheets. Kommt wirklich kein Angriff an Ihrer Blacklist vorbei? Dann haben Sie Glück gehabt. Bisher.

Der bessere Ansatz: Nur Erwünschtes durchlassen

Allgemein besser, da einfacher zu warten, ist eine Positivliste aller zulässigen Eingaben, also eine Whitelist. Wenn HTML erlaubt ist, dürfen nur korrekt formatierte Tags akzeptiert werden. Alles, was nicht wohlgeformt ist, könnte in irgend einem Webbrowser zu unerwünschte Reaktionen führen.

Was nicht auf der Whitelist steht, wird zurückgewiesen oder verworfen. Wobei sich über die Frage, ob die Eingabe kommentarlos verworfen oder mit einer Fehlermeldung abgelehnt wird, streiten lässt. Für beides gibt es gute Gründe:

  • Eine ausführliche Fehlermeldung hilft dem Benutzer, das unerwünschte Zeichen aus seiner Eingabe zu entfernen. Möchte er "Hier ist a<b" eingeben und die Eingabe wird wegen des <-Zeichens nicht akzeptiert, kann er das Zeichen ausschreiben und "Hier ist a kleiner als b" erfolgreich eingeben.
  • Ein Angreifer erfährt durch die gleiche Fehlermeldung aber, woran sein Angriff bei diesem Versuch gescheitert ist und kann daraus evtl. Schlüsse auf weitere mögliche Variationen ziehen.

Mein Rat ist in diesem Fall, der Benutzerfreundlichkeit den Vorzug zu geben. Ein Angreifer lässt sich von einer allgemeinen Meldung wie zum Beispiel "Unerwünschte Zeichen "<b" entdeckt, Eingabe nicht zulässig" nicht abhalten, ein Benutzer von einem mehrmaligen allgemeinen "Eingabe nicht zulässig" aber schnell vergraulen.

Wer gibt denn überhaupt HTML-Code ein?

Seien wir doch mal realistisch: Wie viele "normale" Benutzer können überhaupt HTML-Code eingeben? Also Benutzer, die mit Webentwicklung- und -design nichts zu tun haben? Böse Gerüchte besagen ja, dass sogar viele Webdesigner HTML nur so gut beherrschen wie ihr liebstes Tool es tut.

Meist wird zur Eingabe "gestalteter" Texte doch ein von der Webanwendung im Browser bereit gestelltes Tool verwendet, dass entweder einen vollständigen WYSIWYG-Editor bereit stellt oder das zumindest Hilfestellungen bei der Formatierung der Eingabe liefert.

Das hat den Vorteil, dass die Eingaben im Normalfall in einem bekannten Format vorliegen und gar keine unerwünschten Bestandteile enthalten, was die Filterung deutlich erleichtert. Und um diese Filterung geht es in der nächsten Folge. Denn Sie wissen ja: "Traue nie dem Client" - ein Angreifer muss Ihren Webclient gar nicht nutzen, er kann seinen Schadcode unabhängig davon an die Webanwendung schicken oder die vom Editor erzeugten Daten in einem Proxy manipulieren.

Carsten Eilers

Trackbacks

Keine Trackbacks