molily: Spaß mit Cross-Site Scripting

Hallo,

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

Die Online-Community StudiVZ wurde vermeintlich durch einen XSS-Wurm heimgesucht. Die Programmierer filterten offenbar HTML-Code aus Gruppennamen nicht korrekt. Also fügte jemand fremden, »schädlichen« JavaScript-Code in den Seitencode ein. Dieser Code kommunizierte über XMLHttpRequest mit dem Server - der Browser war ja authentifiziert, also konnte die Identität des Benutzers voll ausgeschöpft werden. Das Script las persönliche Daten aus sowie Daten von »Freunden«. Denen wurde dann eine gefälschte, aber mithilfe der Daten individualisierte und authentisch wirkende Nachricht mit der Link auf die Gruppe geschickt. Fertig war der Community-Wurm, ähnlich dem »Samy«-Wurm damals bei MySpace. Zudem wurde der Benutzer durch ein gefälschtes Login-Formular zum Neu-Einloggen aufgefordert. Die Login-Daten wurden aber nur zusammen mit anderen persönlichen Daten in eine nicht öffentlich zugängliche »Pinnwand« geschrieben - einfach, um dem Betreiber zu zeigen, dass JavaScript diese Daten überall hin hätte verschicken können, natürlich auch an andere Server.

Es war eigentlich recht absehbar, dass das alles passieren kann. Lediglich beim Hinzufügen von Freunden gibt es ein CAPTCHA, da wollte man wohl aus dem Samy-Wurm gelernt haben - geholfen hat es nicht. Die Frage war nur, *wann* jemand eine Stelle findet, an der Benutzereingaben nicht hinreichend überprüft werden. ;) Man kann also nicht vorsichtig genug sein beim Filtern von Benutzereingaben.

Mathias

  1. Hallo Mathias,

    Wir wollen nur nicht hoffen, dass das wieder NPOV-Diskussionen auslöst. ;)

    Tim

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

    StudiVZ ist generell ein schönes Beispiel für vieles :)
    Ich les in diversen Foren zu Programmieren/Internet/Netzwerk oft Fragen von angeblichen "Systemadministratoren" und Programmierern größerer Websites, wo ich mir echt an den Kopf langen muss.

    Heutzutage gibt es einfach vielzuviele Leute die meinen eine Website haben zu müssen, ohne davon etwas zu verstehen... Und noch schlimmer sind die, die meinen etwas davon zu verstehen obwohl dem nicht so ist ^^

    Bei kleinen Websites fällt sowas hald i.d.R. nicht auf, blöd nur, wenn es dann bekanntere Websites trifft :)

    1. Hallo,

      Heutzutage gibt es einfach vielzuviele Leute die meinen eine Website haben zu müssen, ohne davon etwas zu verstehen...

      Ich nehme mal frank und frei an, dass weder du noch ich in einer Liga mit ausgefuchsten XSS-Angreifern mitspielen, daher habe ich mir das übliche Bashing gespart. Schaut lieber mal in den Code eurer schönen Webanwendungen, sollte mein Posting sagen. ;)

      Die MySpace-Leute z.B. waren nicht dumm, sie kannten nur ein abgefahrenes Internet-Explorer-»Feature« nicht und ihre weiteren Vorkehrungen waren einfach auszutricksen. Ich bin darauf auch erst nach der Samy-Geschichte aufmerksam geworden, wenn ich mich recht erinnere. Das heißt, hätte ich eine, wenn auch kleinere Community gehabt, in der vermeintlich harmloses HTML irgendwo zugelassen wäre, wäre diese gleichermaßen verwundbar gewesen. Ohne den Code des StudiVZ-Wurms zu kennen, nehme ich mal an, dass ein ähnliches, vielleicht noch nicht so bekanntes Schlupfloch verwendet wurde.

      Mathias

      1. Ich nehme mal frank und frei an, dass weder du noch ich in einer Liga mit ausgefuchsten XSS-Angreifern mitspielen, daher habe ich mir das übliche Bashing gespart. Schaut lieber mal in den Code eurer schönen Webanwendungen, sollte mein Posting sagen. ;)

        Ich würde von mir nicht behaupten, dass ich das ganze Fehlerfrei machen würde, nein... Aber ich würde mich auch nicht trauen soeine Seite alleine zu programmieren, bzw. sie dann in die Öffentlichkeit zu lassen.
        Allerdings macht StudiVZ ja Fehler, die selbst hier im Forum von den meissten nicht gemacht werden würden. Beispielsweise das mit den Bildern, die über einen Direktlink aufgerufen werden können, obwohl sie privat sind. Oder dass es HTML Tags in irgendwelche Beschreibungsfelder schaffen.

        Nunja, ich bin gespannt wie (und ob) die heutige Panne überstanden wird :)

  3. 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."
    1. Moin!

      Noch eine Ergänzung bzw. Exkursion zum Thema "Kontextwechsel":

      Als geradezu dramatisch (und als Beweis, dass selbst erklärte Profis das Thema nicht im Griff haben) muß man in diesem Zusammenhang die Entwicklung von Word-Viren bezeichnen.

      Früher galt als Benutzertraining gegen Virenabwehr ja die schlichte Regel: Wenn du nicht von _möglicherweise_ bösartigen Programmen infiziert werden willst, dann starte einfach _grundsätzlich_ keine Programme, die man dir z.B. per Mail zuschickt - dann bist du sicher. Alle _Datendateien_ hingegen kannst du beruhigt mit dem zugehörigen Programm öffnen - da passiert nichts.

      Und irgendwann kommt Microsoft auf die Idee, dass man in Word-Dateien auch den Programmcode von Makros speichern kann. Und auf einen Schlag verwandeln sich sämtliche bis dahin als absolut ungefährlich betrachtete Word-Textdateien in Programmdateien - einfach weil die Gleichung "Word-Datei == Text != ausführbarer Programmcode" nicht mehr gilt. Ab sofort mußte man Word-Dateien also als _möglicherweise_ virusverseucht betrachten, und die logische Konsequenz für das Benutzertraining lautete: "Starte keine Programme und öffne keine Dateien, die man dir z.B. per Mail zuschickt".

      Microsoft hat in der Folge den gemachten Fehler dann zu korrigeren versucht - jetzt wird der Anwender immerhin gefragt, ob er Makros in Word-Dokumenten ausführen möchte, oder nicht, und man kann die Makros wohl auch ganz abschalten. Aber wirkliche Sicherheit, dass dieses Feature dann auch garantiert abgeschaltet ist, hat man nicht, sondern muß Microsoft vertrauen, dass die diesen Teil fehlerfrei realisiert haben.

      Das gleiche Spielchen gab's dann übrigens noch mal bei HTML-EMails.

      Eigentlich sind EMails, egal ob Plain Text oder angehübscht durch HTML, nur reiner Text. Den einem Mailprogramm vorzusetzen (beim Mailabruf) und auf dem Bildschirm anzuzeigen sollte _eigentlich_ kein Problem sein.

      Doch Outlook setzt den IE als Mailbetrachter ein, und der IE kann natürlich Javascript ausführen. Außerdem hat der IE immer diverse Sicherheitslücken gehabt, die sich ausnutzen ließen. Und schon passiert wieder so ein Kontextwechsel: Während Plain-Text-Mails weiterhin nur reiner Text sind, können HTML-Mails ausführbaren Code enthalten. Und welchen Inhalt eine Mail hat, sieht man normalerweise vorher nicht. Also muß das Anzeigeprogramm für die HTML-Mails so beschaffen sein, dass ein Ausführen von möglicherweise enthaltenem Programmcode in der Mail garantiert ausgeschlossen ist.

      Die Liste von Sicherheitslücken, die der IE, Outlook und Outlook Express aufweisen (beispielhaft bei Google suchen: http://www.google.com/search?q=outlook+javascript+vulnerability), und von denen viele mit der unerwünschten, schädlichen Ausführung von Javascript (bzw. JScript oder VBScript) in Verbindung stehen, zeigt doch eigentlich recht deutlich, dass diese Kontextwechsel selbst für das größte Softwareunternehmen der Welt nur äußerst schwer beherrschbar sind.

      Es ist für den einfachen Programmierer daher immer eine extrem gute Idee, Kontextwechsel zu vermeiden und immer passend zu Escapen. Insbesondere Text, der immer als harmlos betrachtet werden kann, auch wenn er vielleicht genau die gleichen Zeichen enthält, wie Javascript-Schadcode, darf deshalb niemals als etwas anderes, als Text wirksam werden (insbesondere nicht als Javascript). Nur dann ist man mit recht einfachen Mitteln auf der sicheren Seite.

       - Sven Rautenberg

      -- "Love your nation - respect the others."
      1. Lieber Sven,

        Es ist für den einfachen Programmierer daher immer eine extrem gute Idee, Kontextwechsel zu vermeiden und immer passend zu Escapen. Insbesondere Text, der immer als harmlos betrachtet werden kann, auch wenn er vielleicht genau die gleichen Zeichen enthält, wie Javascript-Schadcode, darf deshalb niemals als etwas anderes, als Text wirksam werden (insbesondere nicht als Javascript).

        sehr fachlich hilfreiche Sachen schreibst Du da! Das sollte das Credo eines jeden Programmierers sein!

        Liebe Grüße aus Ellwangen,

        Felix Riesterer.