Angriffsvektor JavaScript
Transcription
Angriffsvektor JavaScript
Angriffsvektor JavaScript Marcell Dietl Hochschule RheinMain Fachbereich Design Informatik Medien Campus Unter den Eichen 5 65195 Wiesbaden md@marcell-dietl.de ZUSAMMENFASSUNG 1. Das vorliegende Dokument ist im Rahmen des Fachseminars Webbasierte Anwendungen des Studiengangs Angewandte Informatik (B.Sc.) der Hochschule RheinMain im Wintersemester 2013/2014 entstanden. Kaum eine Programmiersprache ist so eng mit dem Begriff des Web 2.0 1 verbunden wie JavaScript. Nicht zuletzt an dieser Verbundenheit dürfte es auch liegen, dass das USUnternehmen RedMonk in seinen Programming Language Rankings [2] JavaScript als die populärste Programmiersprache der Welt ermittelt hat – gemessen an der Anzahl der Projekte auf GitHub und der Tags auf Stack Overflow. Ziel ist es, dem Leser einen kurzen Einblick in die diversen Risiken zu ermöglichen, die der Einsatz von JavaScript – vor allem im Web – mit sich bringt. Neben populären Angriffsvektoren, wie etwa Cross-Site Scripting, werden auch weniger bekannte beispielhaft beleuchtet. Mehreren Beispielen liegt zudem Quellcode bei, mit dem diese einfach und praktisch nachvollzogen werden können. Abschließend folgt ein grober Überblick zu möglichen Abwehrmaßnahmen. Jede Maßnahme wird kurz erläutert und schließlich bewertet. Ausschlaggebend für die Bewertung ist, welchen Aufwand es mit sich bringt, die Abwehrmaßnahme bestmöglich ein- bzw. umzusetzen und welcher Nutzen sich daraus ergibt. EINLEITUNG Betrachtet man sich die Sprach- bzw. Sicherheitskonzepte von JavaScript, wird klar, warum gerade diese Programmiersprache prädestiniert für die Entwicklung von clientlastigen Web 2.0 -Anwendungen zu sein scheint: JavaScript ist eine eingebettete Skriptsprache, die stets in einem Kontext ausgeführt wird, zum Beispiel clientseitig im Browser. Dieser Kontext bestimmt, auf welche Objekte Skriptcode zugreifen kann. Ein Zugriff auf das Dateisystem ist im Browser-Kontext beispielsweise nicht vorgesehen.2 Sehr wohl vorgesehen ist allerdings der Zugriff auf Objekte des Browsers, insbesondere gespeicherte Cookies3 . Die Same Origin Policy (SOP) schränkt den Zugriff von Skriptcode jedoch stark ein: auf jene Cookies der gleichen Herkunft4 . Beide Konzepte, der Kontext eines Skripts und die SOP, scheinen auf den ersten Blick ein ausreichender Schutz zu sein, um JavaScript bedenkenlos einsetzen zu können. Dass dem nicht so ist, belegen die vom Open Web Application Security Project (OWASP) herausgegebenen Top 10 der kritischsten Schwachstellen in Webanwendungen [5]. Auf den Plätzen drei und acht finden sich jene Angriffsvektoren, die in den nächsten Abschnitten besprochen werden: Cross-Site Scripting und Cross-Site Request Forgery. Beides Angriffe, die vorwiegend durch JavaScript und dessen große Verbreitung für Angreifer interessant wurden und meist auf den Missbrauch von Cookies setzen. 1 2 3 4 Inhaltlich und technologisch sehr stark am Nutzer ausgerichtet. Siehe hierzu u. a.: http://de.wikipedia.org/ wiki/Web_2.0 (aufgerufen am 10.11.2013). Statt Kontext spricht man auch von einer Sandbox, in der JavaScript ausgeführt wird. Kleine Textinformationen zur Identifikation von Benutzern. Siehe hierzu u. a.: http://de.wikipedia.org/wiki/ Cookie (aufgerufen am 10.11.2013). Mit Herkunft ist im Falle der SOP das Tripel aus Protokoll, Domain und Port gemeint. 2. ANGRIFFSVEKTOREN In diesem Kapitel werden verschiedene Angriffe, die mit Hilfe von JavaScript realisiert werden können, praxisnah beleuchtet. Manchen Szenarien ist beispielhafter Quellcode beigelegt, damit diese leicht nachvollzogen werden können5 . 2.1 Cross-Site Scripting Der vermutlich bekannteste Angriff, bei dessen Durchführung JavaScript eine wesentliche Rolle spielt, ist das so genannte Cross-Site Scripting (XSS) [4]. Es handelt sich hierbei um einen Spezialfall eines Content-Injection-Angriffs, bei dem es einem Angreifer gelingt, eigenen Skriptcode innerhalb einer vertrauenswürdigen Webseite zu platzieren. Man unterscheidet ferner zwischen min. zwei Arten von XSS: Beim persistenten XSS wird der Skriptcode dauerhaft auf dem Webserver gespeichert, zum Beispiel in Form eines BlogKommentars. Anschließend wird er bei jedem nachfolgenden Seitenaufruf automatisch an alle Besucher ausgeliefert, was zu einer hohen Verbreitung des Skriptcodes führt. Das reflektierte unterscheidet sich vom persistenten XSS dadurch, dass der Skriptcode nicht auf dem Webserver abgelegt wird und der Angriff häufig eine Nutzerinteraktion erfordert, etwa den Aufruf einer manipulierten URL6 . In dieser ist der Skriptcode meist Teil eines Parameters und wird erst beim Aufruf und nur an einen einzigen Besucher ausgeliefert. Für einen erfolgreichen Angriff fehlt nur noch der Schlussstein: Der Cookie muss heimlich an den Angreifer übertragen werden – statt dem Benutzer angezeigt zu werden. Dies lässt sich mit einem recht simplen Standardtrick erreichen: Der Angreifer platziert ein PHP-Skript log.php, das als Cookie Stealer fungiert, auf seinem eigenen Webserver, der zum Beispiel unter http://www.example.org/ erreichbar ist. Dieses nimmt bei jedem Aufruf den GET-Parameter cookie entgegen. Anschließend wird einem Benutzer, ab jetzt auch Opfer genannt, eine URL wie die nachfolgende zugespielt8 . http://www.example.com/search.php?s= <script>document.write(’<img width="0" height="0" src="http://www.example.org/log.php?cookie=’ .concat(document.cookie).concat(’" ></img>’)); </script> Sobald das Opfer die URL aufruft, sendet dessen Browser eine GET-Anfrage an die im src-Attribut des img-Tags angegebene Adresse, um die Ressource zu laden. Mit Hilfe der JavaScript-Methode concat()9 wird dieser Anfrage der Cookie des Opfers beigefügt. Um das offensichtliche Fehlen eines Bildes zu verschleiern, wird dieses auf eine minimale Breite und Höhe von 0 Pixeln reduziert. Beiden Angriffen ist gemein, dass Sie nur funktionieren, wenn eine Webanwendung Nutzereingaben, ob Blog-Kommentar oder URL-Parameter, unzureichend gefiltert wieder ausgibt – zum Beispiel eine Suchanfrage wie folgt beantwortet: Der Angreifer kann sich nun mit Hilfe des Cookies gegenüber der Webseite http://www.example.com/ als das Opfer ausgeben und Aktionen in dessen Namen tätigen. Die Gefährdung, die dadurch entsteht, ist jedoch stark vom Inhalt und der Funktionsweise der Webseite abhängig. Sie suchten nach: <?=$_GET[’s’]?> Eine kleine News-Seite, die Cookies nur zu Werbezwecken speichert, ist für einen Angreifer meist weniger von Interesse als eine große E-Commerce-Seite mit hinterlegten Zahlungsinformationen. Damit der Angreifer auf Letzteres zugreifen kann, müssen jedoch noch min. zwei weitere Bedingungen erfüllt sein: Das Opfer muss zum Zeitpunkt des CookieDiebstahls auch auf der Seite angemeldet gewesen sein und die Seite darf über keine weiteren Sicherheitsvorkehrungen verfügen, zum Beispiel eine zusätzliche Passwort-Abfrage, die beim Anzeigen sensibler Informationen verlangt wird. In diesem sehr einfachen Beispiel wird ein GET-Parameter völlig ungefiltert wiedergegeben. Vorausgesetzt, diese Seite ist über http://www.example.com/search.php erreichbar und hat zuvor mit der Methode setcookie()7 ein Cookie im Browser abgelegt, so lässt sie sich für reflektiertes XSS missbrauchen. Eine typische Test-URL sieht wie folgt aus: http://www.example.com/search.php?s= <script>alert(document.cookie);</script> Ruft ein Benutzer diese URL auf, erscheint ein Pop-upFenster, in dem dieser den Inhalt seines eigenen Cookies angezeigt bekommt. Das funktioniert, da die Webseite beliebigen Skriptcode ungefiltert ausliefert und JavaScript Zugriff auf Cookies der gleichen Herkunft hat, in diesem Fall alle Cookies von http://www.example.com/. 2.2 8 5 6 7 Jeglicher Quellcode ist so weit abstrahiert worden, dass er das Prinzip des Angriffs zwar deutlich macht, an und für sich jedoch harmlos ist. Uniform Resource Locator. Siehe hierzu u. a.: http:// tools.ietf.org/html/rfc1738 (aufgerufen am 10.11.2013). Siehe hierzu u. a.: http://php.net/manual/de/function. setcookie.php (aufgerufen am 10.11.2013). Cross-Site Request Forgery Auf Platz acht der OWASP Top 10 der kritischsten Schwachstellen in Webanwendungen [5] ist ein Angriff, der als CrossSite Request Forgery (CSRF, manchmal auch XSRF) [3] bezeichnet wird. Ziel dieses Angriffs ist es, einen Benutzer eine Aktion ohne dessen Wissen ausführen zu lassen – ihn zum Beispiel zu einer Änderung des DNS10 -Server-Eintrags in seinem Router zu veranlassen. Denn dann kann ein Angreifer den gesamten Internetverkehr nach Belieben lenken 9 10 Um keinen Verdacht zu erwecken, können längliche URLs mit Hilfe von Kurz-URL-Diensten verschleiert werden. Siehe hierzu u. a.: http://de.wikipedia.org/ wiki/Kurz-URL-Dienst (aufgerufen am 11.11.2013). Siehe hierzu u. a.: http://de.selfhtml.org/javascript/ objekte/string.htm (aufgerufen am 11.11.2013). Domain Name System. Siehe hierzu u. a.: http://de. wikipedia.org/wiki/Domain_Name_System (aufgerufen am 12.11.2013). und die Zugangscodes besonders lohnenswerter Seiten mittels Phishing11 abzugreifen versuchen. Es lassen sich hierbei min. zwei Arten von CSRF-Angriffen unterscheiden: Jene, die eine GET-Anfrage absetzen und auch ganz ohne JavaScript funktionieren und jene, bei denen das Versenden einer POST-Anfrage benötigt wird, um die Manipulation durchzuführen. Letzteres lässt sich mit Hilfe von JavaScript besonders leicht automatisieren und verheimlichen. Angenommen unter dem Hostnamen router ist das Webinterface eines ungenügend geschützten Routers zu erreichen. Ungenügend meint in diesem Fall, dass das Webinterface keine Passworteingabe verlangt und die Datei edit.php für CSRF anfällig ist. Dieser Datei kann sowohl via GET als auch POST der Parameter dns übergeben werden, der den DNS-Server-Eintrag des Routers überschreibt. Der Standardtrick besteht nun darin, dem Opfer eine HTMLE-Mail zu schicken, in der ein Bild mit einer Breite und Höhe von 0 Pixeln versteckt ist – ganz ähnlich dem Trick zum Diebstahl von Cookies in Abschnitt 2.1: Aber die wichtigste Bedingung von allen ist die, welche zu Anfang schlicht unterstellt wurde: Das Wissen um den Hostnamen bzw. die IP-Adresse und das Vorhandensein eines ebensolchen Webinterfaces auf Seiten des Opfers. Bei genauerer Betrachtung ist Letzteres jedoch gar keine allzu große Hürde. Viele Router sind über bekannte Hostnamen bzw. IP-Adressen zu erreichen. Findet ein Angreifer in einem der verbreiteten Modelle eine Lücke, wird der Massenversand von E-Mails zwar zu vielen fehlgeschlagenen Angriffen führen, aber immer noch zu einer beträchtlichen Menge manipulierter Router. Zudem sind CSRF-Angriffe nicht auf Router beschränkt, sondern bereits mehrfach in bekannten Webseiten und sozialen Netzwerken vorgekommen. Und selbst zu streng eingestellte E-Mail-Clients sind keine wirkliche Hürde, wenn das Opfer mit ein wenig Social Engineering14 zum Aufruf einer Webseite bewegt werden kann, in welcher dann der eigentliche Angriffscode zur Ausführung kommt. 2.3 Browser History Stealing <img width="0" height="0" src="http://router/edit.php?dns=1.3.3.7"> Anfang 2010 wurden im Mozilla Security Blog unter dem Titel Plugging the CSS History Leak [6] eine Reihe von Änderungen im hauseigenen Browser Firefox bekanntgegeben, mit denen gleich drei Angriffen begegnet werden sollte. Alle drei stellten eine Gefahr für die Privatsphäre der Nutzer dar, da sie die Liste besuchter Webseiten auszulesen versuchten. Lädt der E-Mail-Client des Opfers externe Bilder automatisch nach oder wurde dies explizit erlaubt, wird das Laden des vornehmlichen Bildes zu einer GET-Anfrage führen, die den DNS-Server-Eintrag des Routers überschreibt. Voraussetzung ist, dass das Opfer die E-Mail auf einem Rechner betrachtet, der sich im gleichen Netz wie der Router befindet, sodass der Hostname router auch erreichbar ist. Ein direkter Zugriff auf die History15 ist zwar nicht möglich gewesen, aber durch geschicktes Kombinieren mehrerer Techniken konnte eine Liste vordefinierter URLs abgefragt und je nach Reaktion des Browsers erkannt werden, ob der Nutzer eine URL bereits besucht hatte oder nicht. Will der Angreifer eine POST- statt einer GET-Anfrage absetzen, so kann er beispielsweise auf die freie JavaScriptBibliothek jQuery12 zurückgreifen und sich der dortigen Methode post()13 bedienen: <script> $.post("http://router/edit.php", { dns: "1.3.3.7" } ); </script> Beiden Varianten des beispielhaft durchgeführten Angriffs ist gemein, dass sie nur dann ideal funktionieren, wenn eine Reihe von Faktoren zusammenkommen. Neben der Grundvoraussetzung eines anfälligen und möglichst ungeschützten Webinterfaces, bedarf es eines Opfers mit einem E-MailClient, der Ressourcen automatisch nachlädt und Skriptcode ungefragt ausführt. Außerdem muss das Opfer die E-Mails im richtigen Netz betrachten. 11 12 13 Kunstwort, mit dem das Abfischen von Passwörtern gemeint ist. Siehe hierzu u. a.: http://de.wikipedia.org/ wiki/Phishing (aufgerufen am 12.11.2013). Siehe hierzu u. a.: http://jquery.com/ (aufgerufen am 12.11.2013). Siehe hierzu u. a.: http://api.jquery.com/jQuery.post/ (aufgerufen am 12.11.2013). Die ersten beiden Angriffe, denen in [6] Rechnung getragen wird, Layout Based Attacks und Timing Attacks, sollen hier nicht weiter vertieft werden, da sie weniger JavaScript als Angriffsvektor nutzten als vielmehr CSS16 bzw. zeitliche Unterschiede beim Auswerten besuchter und nicht besuchter Seiten. Der letzte im Blogeintrag beschriebene Angriff, Computed Style Attacks, wäre ohne JavaScript jedoch nicht möglich gewesen, da er im Kern auf die JavaScript-Methode window.getComputedStyle()17 angewiesen ist. Mit dieser Methode lassen sich die CSS-Eigenschaften eines jeden Seitenelements ermitteln, nachdem die in den CSSDateien hinterlegten Vorschriften angewendet wurden. Im Falle der Computed Style Attacks waren diese Vorschriften minimal: Mit Hilfe der Link-Pseudoklassen :link und :visited 14 15 16 17 Beeinflussung bzw. Manipulation von Menschen. Siehe hierzu u. a.: http://de.wikipedia.org/wiki/Social_ Engineering_(Sicherheit) (aufgerufen am 12.11.2013). Gleichsam der Name eines JavaScript-Objekts, das eingeschränkten Zugriff auf die Liste besuchter Webseiten ermöglicht. Siehe hierzu u. a.: https://developer. mozilla.org/en-US/docs/Web/API/window.history (aufgerufen am 12.11.2013). Cascading Style Sheets. Siehe hierzu u. a.: http://de. wikipedia.org/wiki/Cascading_Style_Sheets (aufgerufen am 12.11.2013). Siehe hierzu u. a.: https://developer.mozilla.org/enUS/docs/Web/API/Window.getComputedStyle (aufgerufen am 12.11.2013). wurden noch nicht besuchten und bereits besuchten Webseiten unterschiedliche Farben zugewiesen. Der Angriff bestand dann in einer Schleife, die eine Liste vordefinierter URLs abgearbeitet hat und ihren Farbwert etwa wie folgt ermittelte: window.getComputedStyle(link, null). getPropertyValue("color") Entsprach die Farbe der in link hinterlegten URL der von :visited, konnte ein Angreifer ziemlich sicher sein, dass der Nutzer diese Webseite besucht hatte. Eine Information, die vor allem zu Werbezwecken missbraucht werden kann, da sich aus der Liste besuchter Webseiten ein Persönlichkeitsprofil ableiten lässt. Um dem Problem Herr zu werden, ist es Browsern seit Version 2.1 der CSS-Spezifikation erlaubt, alle Links wie nicht besuchte Links zu behandeln oder andere Maßnahmen zu ergreifen, um die Privatsphäre der Nutzer zu schützen 18 . Bezogen auf die im Beispiel verwendete JavaScript-Methode window.getComputedStyle() bedeutet das konkret, dass Firefox diese bzgl. der Farbe der Links täuscht. Die Methode liefert stets die Farbe von :link zurück. Somit erscheinen alle Links als wären sie noch nicht besucht worden. 2.4 Web Proxy Detection Mitte 2013 veröffentlichte der VPN-Anbieter ZorroVPN auf seiner Webseite unter dem Titel Web proxy detection and real IP address disclosure [10] einen Artikel, der mehrere Wege beschreibt, mit der die Anonymität der Nutzer von Web Proxys19 untergraben werden kann. Voraussetzung ist lediglich, dass JavaScript im Web Proxy und Browser des Nutzers aktiviert ist – was den Normalfall darstellt. Angenommen in der Variablen our_host ist der Hostname des Angreifers – example.org – hinterlegt und ein Benutzer ruft eine dort hinterlegte Seite über einen beliebigen Web Proxy auf. Dann reicht schon eine einzige Zeile JavaScript aus, damit der Angreifer in Erfahrung bringen kann, ob der Benutzer einen Web Proxy verwendet oder nicht: if (window.location.hostname != our_host) { ... } Trifft die Bedingung zu, weiß der Angreifer, dass seine Seite nur indirekt, eingebettet in eine andere Seite, den Web Proxy, aufgerufen wird. Der Trick besteht schlicht darin, dass JavaScript ungefiltert clientseitig ausgeführt wird und die Eigenschaft hostname20 den wahren Hostnamen verrät. Ferner lässt sich nicht nur ermitteln, von wo die eigene Seite aufgerufen wird, sondern auch, welcher Web Proxy zum Einsatz kommt. Dazu reicht es aus, mit Hilfe von JavaScript 18 19 20 In eigenen Worten wiedergegeben. Originalwortlaut zu finden unter: http://www.w3.org/TR/CSS2/selector. html#link-pseudo-classes (aufgerufen am 12.11.2013). Gemeint sind jene Proxys, die wie eine reguläre Webseite ausschließlich im Browser arbeiten. Siehe hierzu u. a.: http://de.selfhtml.org/javascript/ objekte/location.htm (aufgerufen am 13.11.2013). das Vorhandensein spezieller Werte zu überprüfen, die sich von Web Proxy zu Web Proxy unterscheiden. Die drei im Artikel getesteten lassen sich beispielsweise wie folgt voneinander unterscheiden und eindeutig identifizieren – jede Zeile trifft auf einen anderen Web Proxy zu: if (window["REAL_PROXY_HOST"]) { ... } if (window["_proxy_jslib_SCRIPT_URL"]) { ... } if (typeof ginf != ’undefined’) { ... } Zwar ist dieser Trick nicht so generisch, wie der zuvor vorgestellte, dennoch sollte er sich mit nur geringem Aufwand auf die bekanntesten öffentlich verfügbaren Web Proxys anwenden lassen. Dass er funktioniert, ist verständlich, wenn man sich bewusst macht, wie ein Web Proxy grob funktioniert: Jede Seite wird zuerst vollständig geladen und hinsichtlich externer Ressourcen bzw. vorhandener Links untersucht. Jeder gefundene Link wird so abgeändert, dass ein Aufruf von diesem wieder über den Web Proxy erfolgt. Anschließend wird die Seite, eingebettet in den Code des Web Proxys, dem Nutzer angezeigt. Und mit Hilfe von JavaScript lässt sich nun clientseitig der sie umgebende Code des Web Proxys auf verräterische Werte hin untersuchen. Doch nicht nur JavaScript selbst wird ungefiltert bzw. ungenügend gefiltert an den Nutzer weitergegeben: Eine geschickte Maskierung eines Links innerhalb von JavaScript kann dazu führen, dass dieser vom Web Proxy erst gar nicht erkannt und somit auch nicht abgeändert wird. Sobald der JavaScript-Code clientseitig ausgeführt wird, lädt der Browser die Ressource nicht über den Web Proxy, sondern direkt, wodurch die echte IP-Adresse des Nutzers erkennbar ist. Auch dieser Trick ist nicht so generisch, wie der zuerst vorgestellte, dennoch ließ er sich auf alle in [10] untersuchten Web Proxys mit nur geringen Unterschieden anwenden. Die Vermutung liegt somit nahe, dass andere Web Proxys ebenfalls dergestalt getäuscht werden können. 2.5 Server-Side JavaScript In allen bisher behandelten Abschnitten wird JavaScript clientseitig ausgeführt und die Angriffe, die dadurch möglich sind, stellen größtenteils eine Gefahr für den Nutzer, zumeist in der Rolle des Besuchers einer Webseite, dar. Doch mit dem Aufkommen von Frameworks wie Node.js21 wird JavaScript in zunehmendem Maße auch auf Seiten der Server eingesetzt. Die Gefahren, die dadurch entstehen, fasst Sven Vetsch in seiner OWASP-Präsentation Node.js Security: Old vulnerabilities in new dresses [7] eindrucksvoll zusammen. Anknüpfend an das Beispiel in Abschnitt 2.1 wird erneut von einer Webanwendung ausgegangen, welche die Suchanfragen der Nutzer ungenügend filtert. Genauer gesagt: den Inhalt des URL-Parameter s ungefiltert reflektiert. Die Seite lausche wie gewohnt auf Port 8022 einer fiktiven URL. 21 22 Siehe hierzu u. a.: http://nodejs.org/ (aufgerufen am 13.11.2013). Standard-Port des Hypertext Transfer Protocols. Siehe hierzu u. a.: http://de.wikipedia.org/wiki/Hypertext_ Transfer_Protocol (aufgerufen am 16.11.2013). Zum Verständnis sei folgender Quellcode gegeben: var http = require(’http’); var url = require(’url’); Der Server antwortet daraufhin mit: Sie suchten nach: undefined. Im Hintergrund jedoch, in der Kommandozeile, in der Node.js gestartet wurde, erscheint der Inhalt des aktuellen Arbeitsverzeichnisses26 . Mit ein wenig mehr Fantasie kann dieser Angriff so weit ausgebaut werden, dass ein Angreifer vollständige Kontrolle über den Rechner erlangt. http.createServer(function (req, res) { res.writeHead(200, {’Content-Type’: ’text/html’}); var s = eval(url.parse(req.url, true).query.s); res.end(’Sie suchten nach: ’ + s.search); }).listen(80); Nach dem Laden der Module http und url startet der obige Quellcode einen Server, der auf ankommende Anfragen wartet, um diese mit dem Status-Code 20023 und der Ausgabe einer Zeile Text zu beantwortet. An dessen Ende ist der Inhalt des zuvor extrahierten URL-Parameters s angehängt. 2.6 Die Liste ist keinesfalls vollständig, besonders da JavaScript in immer größerem Umfang in immer mehr Bereichen zum Einsatz kommt, beispielsweise als Programmiersprache für Anwendungen mobiler Endgeräte oder als Teil von SQL27 Statements zum Abfragen von Datenbank-Inhalten. 3. Dieser erwartet jedoch keine simple Zeichenkette, sondern ein in JSON24 angegebenes JavaScript-Objekt. Eine TestURL zum Verifizieren der augenscheinlichen Anfälligkeit für XSS-Angriffe sieht beispielsweise wie folgt aus: http://www.example.com/?s= ({’search’:’<script>alert();</script>’}) Deutlich problematischer ist die immer noch weit verbreitete Angewohnheit, empfangenes JSON mit Hilfe der Methode eval()25 zu verarbeiten, um auf die Key-Value-Paare mittels Punktnotation zugreifen zu können. Zur Verdeutlichung der Problematik werde folgende Test-URL aufgerufen: http://www.example.com/?s= ({’search’:2*2}) Der Server antwortet daraufhin mit: Sie suchten nach: 4. Er hat den Inhalt folglich nicht nur verarbeitet, sondern auch interpretiert. Bei einer einfachen Rechenaufgabe mag diese Angewohnheit nicht allzu gefährlich, vielleicht sogar nützlich, wirken. Doch eval() macht keinen Unterschied zwischen dem Lösen einer Multiplikation und dem Ausführen eines Kommandozeilen-Befehls, wie das folgende Beispiel zeigt: http://www.example.com/?s= require(’child_process’).exec ("dir", function (error, stdout, stderr) { console.log(stdout); }) 23 24 25 Entspricht OK. Siehe hierzu u. a.: http://tools.ietf.org/ html/rfc1945 und http://tools.ietf.org/html/rfc2616 (aufgerufen am 16.11.2013). JavaScript Object Notation. Siehe hierzu u. a.: http: //de.wikipedia.org/wiki/JavaScript_Object_Notation (aufgerufen am 16.11.2013). Siehe hierzu u. a.: http://de.selfhtml.org/javascript/ objekte/unabhaengig.htm (aufgerufen am 16.11.2013). Weitere Angriffsvektoren Alle bisher aufgezeigten Schwachstellen und die daraus resultierenden Angriffe stellen lediglich eine grobe Auswahl typischer Risiken dar, die der Einsatz von JavaScript mit sich bringt. ABWEHRMAßNAHMEN In diesem Kapitel werden verschiedene Maßnahmen beleuchtet und bewertet, mit denen den zuvor beschriebenen Risiken, die der Einsatz von JavaScript mit sich bringt, serversowie clientseitig begegnet werden kann. Die Maßnahmen sind als typische Vertreter verschiedener Ansätze zu verstehen und keinesfalls vollständig. 3.1 JavaScript abschalten / filtern Einer der wohl radikalsten Ansätze, um sich als Nutzer vor den meisten der bekannten JavaScript-Risiken zu schützen, ist das clientseitige Abschalten bzw. Filtern von JavaScript. Für Firefox steht beispielsweise das Add-on NoScript28 zur Verfügung, das bereits über 2 Million Mal heruntergeladen wurde und sowohl das Abschalten als auch das Filtern von JavaScript ermöglicht. Wenngleich das Abschalten viele Angriffe effektiv verhindert, werden auch zahlreiche reguläre Web 2.0 -Anwendungen ohne JavaScript anders als gewohnt oder gar nicht mehr funktionieren. Für diese kann zwar anschließend eine Ausnahmeregel definiert werden, sodass sie JavaScript ausführen dürfen, aber dann sind auch wieder die meisten der eigentlich zu verhindernden Angriffe möglich. Und mit dem Willen der Betreiber, JavaScript-freie Alternativen ihrer Web 2.0 -Anwendungen zu bieten, ist auch nicht zu rechnen. Denn schon 2010 ermittelte Yahoo! in einer Studie [9], dass nur etwa 1 % der eigenen Besucher JavaScript tatsächlich abgeschaltet hatten. Hinzu kommt, dass JavaScript nicht mehr nur im Web Anwendung findet und ein Filtern aller erdenklichen Einsatzbereiche eine recht aufwendige Schutzmaßnahme darstellt, die für weniger technikaffine Nutzer keine Option ist. Fazit: In Einzelfällen ist das Abschalten bzw. Filtern von JavaScript zwar sinnvoll, eine Lösung ist es aber nicht. 26 27 28 dir ist das Windows-Äquivalent des Linux-Befehls ls. Structured Query Language. Siehe hierzu u. a.: http://de.wikipedia.org/wiki/SQL (aufgerufen am 16.11.2013). Siehe hierzu u. a.: http://noscript.net/ (aufgerufen am 25.11.2013). 3.2 Bug-Bounty-Programme29 Einen recht speziellen Weg, um Schwachstellen zu begegnen, geht die Zero Day Initiative (ZDI)30 : Sie dient als Bindeglied zwischen IT-Unternehmen und jenen, die Schwachstellen in deren Produkten finden und ermöglicht Letzteren, diese anonym und für Geld zu verkaufen. Den Herstellern bietet dieses Modell den großen Vorteil, dass das Wissen um Schwachstellen in ihren Produkten nicht in falsche Hände gerät und sie rechtzeitig reagieren können. Neben der ZDI gibt es bereits mehrere Unternehmen, die eigene Bug-Bounty-Programme betreiben und Entdecker von Schwachstellen für das verantwortungsvolle Melden 31 ebenjener entlohnen. So zahlt etwa Google bis zu 7500 USD für gemeldete XSS-Schwachstellen der eigenen Diensten [1]. Fazit: Lohnender Ansatz, der die Sicherheit deutlich erhöht, da gefundene Schwachstellen den Herstellern gemeldet und von diesen zeitnah behoben werden können. 3.3 Secure Coding Ein ebenso nachhaltiger wie auch aufwendiger Ansatz, um Schwachstellen zu begegnen, stellt das sichere Programmieren aller Anwendungen dar. Zwar lässt sich dessen Grundgedanke – Traue keiner Benutzereingabe! – einfach formulieren, die Umsetzung ist aber zumeist deutlich komplexer. Damit der vorgeschlagene Mechanismus greift, muss dieser jedoch sowohl auf der Client- als auch auf der Server-Seite unterstützt werden. Grund ist die Einführung des neuen HTTP-Headers Content-Security-Policy, der vom Server gesendet, aber auch vom Client verstanden werden muss. Mit diesem Header können Webanwendungen angeben, von wo Inhalte, z. B. Bilder oder JavaScript, die innerhalb der Seite genutzt werden, geladen werden dürfen. Ferner wird eine strikte Trennung von Code und Daten angestrebt und vom Verwenden besonders problematischer Methoden wie eval() abgeraten. Eine ideale Umsetzung würde z. B. die zuvor vorgestellten XSS-Angriffe unterbinden, da sie auf dem Einfügen von Code innerhalb der Webseite beruhen. Doch ähnlich dem in Abschnitt 3.3 vorgeschlagenen sicheren Programmieren als Abwehrmaßnahme, ist auch die Einführung von CSP und die damit verbundene Überarbeitung bestehender Anwendungen eine zumeist zeitintensive Aufgabe, die einer schnellen Verbreitung im Wegen stehen dürfte. Fazit: Wenngleich viele der angestrebten Maßnahmen, etwa die strikte Trennung von Code und Daten, unabhängig vom Erfolg von CSP eine gute Entscheidung darstellen, bleibt fraglich, ob sich dieser Mechanismus, der sowohl vom Server als auch vom Client, herstellerübergreifend umgesetzt werden muss, letztlich durchsetzen wird. Damit Programme konsequent sicher programmiert werden können, muss das Bewusstsein für Sicherheit fester Bestandteil des gesamten Entwicklungs- und Lebenszyklus von Software sein – von Anfang an. Auf veraltete Systeme, Programme und Methoden muss verzichtet und diese durch sichere Alternativen ersetzt werden – auch auf Kosten der Kompatibilität, z. B. zu älteren Browsern. 4. Häufig steht dem Willen, sichere Software zu entwickeln und auf veraltete Techniken zu verzichten jedoch der Wunsch nach einer möglichst großen Nutzerschaft bzw. das Vorhandensein von Legacy-Systemen32 im Weg. Auch die Schwierigkeit, sich unter Konkurrenten auf einen gemeinsamen sicheren Standard zu einigen, führt immer wieder zum Rückgriff auf bewährte unsichere Standards. Idealerweise wird gerade bei neu entstehenden Webanwendungen die Chance genutzt, schon während der Entwicklung auf Sicherheit Wert zu legen und diese während des gesamten Lebenszyklus im Blick zu behalten. Fazit: Langfristig ist guter, sicherer Code auf Client- und Server-Seite allen anderen Abwehrmaßnahmen vorzuziehen. 3.4 Content Security Policy Eine recht neue Möglichkeit, Content-Injection-Angriffen wie XSS zu begegnen, stellt ein vor allem von Google und Mozilla vorangetriebener Mechanismus namens Content Security Policy (CSP) dar, dessen erste Version [8] bereits den W3C33 -Status Candidate Recommendation erreicht hat. 29 30 31 32 33 Bug ist engl. für Fehler. Bounty ist engl. für Prämie. Siehe hierzu u. a.: http://www.zerodayinitiative.com/ (aufgerufen am 16.11.2013). Häufig als Responsible Disclosure bezeichnet. Siehe hierzu u. a.: http://en.wikipedia.org/wiki/Responsible_ disclosure (aufgerufen am 16.11.2013). Altsysteme. Siehe hierzu u. a.: http://de.wikipedia.org/ wiki/Legacy-System (aufgerufen am 16.11.2013). World Wide Web Consortium. Siehe hierzu u. a.: http: //www.w3.org/ (aufgerufen am 16.11.2013). FAZIT Wenngleich der Einsatz von JavaScript viele Risiken mit sich bringt, wird dessen Verwendung kurz- bis mittelfristig wahrscheinlich noch weiter zunehmen. Vor allem dank Frameworks wie jQuery und Node.js ist JavaScript nicht nur leichter zu bedienen, sondern auch für ganz neue Aufgabenbereiche interessant geworden. Die Zunahme an Bug-Bounty-Programmen und die Einführung von CSP zeigen bereits ein gestiegenes Bewusstsein für Sicherheit und einen positiven Umgang mit denjenigen, die auf Schwachstellen hinweisen. 5. LITERATURVERZEICHNIS [1] A. Mein; M. Zalewski. Increased rewards for Google’s Web Vulnerability Rreward Program. http://googleonlinesecurity.blogspot.de/2013/06/ increased-rewards-for-googles-web.html (aufgerufen am 16.11.2013). [2] S. O’Grady. The RedMonk Programming Language Rankings: January 2013. http://redmonk.com/sogrady/2013/02/28/languagerankings-1-13/ (aufgerufen am 10.11.2013). [3] OWASP. Cross-Site Request Forgery (CSRF). https://www.owasp.org/index.php/Cross-Site_ Request_Forgery_(CSRF) (aufgerufen am 11.11.2013). [4] OWASP. Cross-Site Scripting (XSS). https://www.owasp.org/index.php/Cross-site_ Scripting_(XSS) (aufgerufen am 10.11.2013). [5] OWASP. Top 10 2013. https://www.owasp.org/index.php/Top_10_2013Top_10 (aufgerufen am 10.11.2013). [6] S. Stamm. Plugging the CSS History Leak. http://blog.mozilla.org/security/2010/03/31/ plugging-the-css-history-leak/ (aufgerufen am 12.11.2013). [7] S. Vetsch. Node.js Security: Old vulnerabilities in new dresses. https://www.owasp.org/images/3/31/Node.js_ Security_Old_vulnerabilities_in_new_bottles__Sven_Vetsch.pdf (aufgerufen am 13.11.2013). [8] W3C. Content Security Policy 1.0. http://www.w3.org/TR/CSP/ (aufgerufen am 16.11.2013). [9] N. C. Zakas. How many users have JavaScript disabled? http://developer.yahoo.com/blogs/ydn/many-usersjavascript-disabled-14121.html (aufgerufen am 16.11.2013). [10] ZorroVPN. Web proxy detection and real IP address disclosure. https://zorrovpn.com/articles/web-proxy-detection (aufgerufen am 12.11.2013).