Sven Rautenberg: Spaß mit Cross-Site Scripting

Beitrag lesen

Moin!

Eigentlich eine sehr nette Anekdote, die zeigt, wie zentral JavaScript und XMLHttpRequest auch für die Sicherheit geworden sind

Die Lehre, die man aus solchen Szenarien (StudiVZ-Doku steht ja noch aus, die MySpace-Geschichte ist aber ja ziemlich ausführlich dokumentiert - mein Respekt für die Autoren!) ziehen kann, ist aber zum Glück sehr simpel und aufgrund ihrer Simplizität auch von jedermann absolut einfach umzusetzen.

Die Lehre lautet:
1. Kontextwechsel sind böse und
2. Escaping immer passend zum Kontext vornehmen.

Erklärung:

Was ist ein Kontextwechsel?
Ein Wechsel des Kontext findet statt, wenn Systeme oder Umgebungen sich ändern. Klingt zunächst mal abstrakt, aber am Beispiel wird es deutlich:

In eine Textarea oder ein Input-Feld kann man Text eingeben. Simplen, unformatierten, ggf. mit Leerzeilen versehene Zeichenfolgen. Jedes Zeichen hat genau die Bedeutung, die man als Mensch dem Zeichen zuordnet, ein A ist ein A und bedeutet A.
Ein doppeltes Anführungszeichen ist ein " und bedeutet etwas wie "Beginn oder Ende wörtlicher Rede".
Ein einfaches Anführungszeichen ist ein ' und bedeutet ungefähr das gleiche.
Ein ist-kleiner-Zeichen ist ein < und bedeutet "mathematisches Symbol für 'ist kleiner als'".

Jetzt denken wir uns irgendeine Webapplikation, die eine Textarea enthält. Dort gibt der Benutzer etwas ein, und schickt die Daten ab. Die Applikation zeigt dem Benutzer nochmal die eingegebenen Daten als Vorschau an.

Was passiert hierbei? Der Kontext wechselt! Denn während der Inhalt der Textarea nur schlichter Text ist, befindet sich die erneute Ausgabe dieses Textes jetzt im HTML-Kontext. Und in diesem Kontext haben einige Zeichen eine vollkommen andere Bedeutung.

Ein A ist ein A und bedeutet A - keine Änderung.
Ein doppeltes Anführungszeichen ist ein " und bedeutet "Beginn oder Ende von Attributwerten, wenn es innerhalb von HTML-Tags auftaucht - ansonsten harmloses doppeltes Anführungszeichen, das 1:1 als Text ausgegeben wird.
Beim einfachen Anführungszeichen ist es genau das Gleiche.
Und ein ist-kleiner-Zeichen ist ein < und bedeutet "Beginn eines HTML-Tags".

Würde man also einfach den Text aus der Textarea 1:1 wieder ausgeben, würde man, sollten im Text Zeichen vorkommen, die im HTML-Kontext eine ganz andere Bedeutung haben, als im Text-Kontext, mindestens mal Murks erhalten.

Deshalb ist es notwendig, alle Zeichen im Text, die im HTML-Kontext eine andere Bedeutung haben, so zu entschärfen, dass sie durch Zeichen oder Zeichenkombinationen ersetzt werden, die im HTML-Kontext exakt die gleiche Bedeutung haben, wie im Text-Kontext.

Das bedeutet für das ist-kleiner-Zeichen, dass es im HTML-Kontext als &lt; geschrieben werden muß. Denn die Entity &lt; steht im HTML-Kontext für "mathematisches Symbol für 'ist kleiner als'". Ebenso ist das doppelte Anführungszeichen lieber konsequent durch die Entity &quot; zu ersetzen, denn nur diese Entity bedeutet überall im Quelltext "Beginn oder Ende wörtlicher Rede" - auch innerhalb von HTML-Tags.

Man muß also im Prinzip einfach nur aufpassen, dass ein Text, der im Text-Kontext eingegeben wird (Textarea), immer und durchgehend nur als Text aufgefaßt wird - was in anderem Kontext (HTML) bedeutet, dass man sämtliche Zeichen im Text so escaped, dass keinerlei Verwechslungsmöglichkeit mit HTML besteht. Das ist im Prinzip auch recht einfach machbar, da nur die vier Zeichen <, >, & und " in HTML abweichend interpretiert werden und somit in Entities gewandelt werden müssen. PHP bietet mit der Funktion htmlspecialchars() eine vorgefertigte Escape-Funktion an, andere Programmiersprachen haben mit Sicherheit alternative Möglichkeiten oder werden durch Eigenbau entsprechend ausgestattet.

Was aber, wenn man dem Textautoren eine gewisse Formatierungsmöglichkeit einräumen möchte? Simple Antwort: Dann wird's extrem kompliziert und gefährlich! Insbesondere dann, wenn man den Absichten des Autoren nicht trauen kann und ihm (zumindest einigen der potentiellen Nutzer) böse Absichten unterstellen muß, sollte man sich der Problematik des Kontextwechsels sehr bewußt sein.

Wenn man die eingangs erwähnte Textarea nicht als Text- sondern als HTML-Quellcodeeditor betrachtet, hat man es zunächst mal recht einfach: Der Autor muß HTML können - und wenn er freundlich gesinnt ist, wird er keinen Scheiß bauen. Das Ergebnis der Textarea kann dann problemlos ohne Escaping direkt in die HTML-Seite ausgegeben werden.

Aber wie verhindere ich gezielt, dass der Autor keinen Javascript-Code in sein HTML einbaut? Simple Antwort: Einfach rausfiltern. Rückfrage: Wie denn filtern? Komplizierte Antwort: So einfach ist das nicht.

Das Filterprogramm muß erkennen, welche Zeichenfolge im Quelltext "böses Javascript" ist, und sie herauslöschen. Die Schwierigkeit dieser Aufgabe liegt nicht darin, das ganz offensichtliche Muster "<script ......</script>" zu finden und zu entfernen. Die Problematik liegt darin, dass die existierenden Browser noch an hundert anderen Stellen im HTML-Quelltext ebenfalls Javascript erkennen und ausführen, von denen man ebenfalls wissen muß. Der Internet-Explorer führt beispielsweise auch Javascript aus, dass sich in der URL einer CSS-Hintergrundgrafikeinbindung befindet:

<p style="background-image:url('javascript:alert(document.URL)')">Irgendein Text</p>

Das bedeutet: Wenn man erlaubt, dass gewisse HTML-Elemente vom Filter durchgelassen werden, muß man auch peinlichst genaue Vorschriften zu den erlaubten Attributen und Attributwerten machen - ansonsten wäre der Filter überwindbar.

Eine recht gute Lösung ist daher, statt des direkten Transfers von gefiltertem HTML eine Metasprache wie BBCode zur Formatierung einzusetzen. Sofern die Umsetzung von BBCode in formatierendes HTML keinen Text aus dem BBCode ungefiltert in HTML-Elemente einfügt, scheint das dadurch erreichte Sicherheitsniveau schon verhältnismäßig gut. Es hängt allerdings extrem von der Art und Weise ab, wie BBCode in HTML transformiert wird.

Deshalb sei gewarnt: Solche Code-Injection-Angriffe werden von erfahrenen Profis durchgeführt. Nicht alle sind so freundlich, es als sportliche Herausforderung zu sehen, und keinen Schaden anzurichten. Wer also selbst kein Profi ist oder sich nicht die Zeit nehmen will, komplizierte Filtermechanismen zu konstruieren, dem sei dringend davon abgeraten, vom Benutzer eingebbaren formatierbaren Text zu realisieren, und ihm sei dringend dazu geraten, sich auf die schlichte Nutzung von Text, passend codiert zum Kontext, zu beschränken.

- Sven Rautenberg

--
"Love your nation - respect the others."