Forschungsbericht 2013 - Max-Planck-Institut für Softwaresysteme, Standort Kaiserslautern

Verbesserung der JavaScript-Sicherheit durch Informationsflussanalyse

Improving JavaScript security with information flow analysis

Autoren
Garg, Deepak
Abteilungen
Abteilung: Foundations of Computer Security / Max-Planck-Institut für Softwaresysteme, Saarbrücken / Max-Planck-Institut für Softwaresysteme, Kaiserslautern
Zusammenfassung
JavaScript ist eine Programmiersprache, die alle wichtigen Webbrowser interpretieren können. Aufgrund seiner großen Verbreitung, funktional vielfältigen Schnittstellen und relativ schwachen Schutzmechanismen wird JavaScript häufig für Angriffe ausgenutzt, bei denen die Vertraulichkeit und Unversehrtheit sensibler Daten im Browser verletzt wird – Passwörter, Kreditkartennummern, Cookies usw. Dieser Bericht stellt die aktuelle Arbeit zum Schutz von JavaScript mittels Informationsflussanalyse vor, einer Methode, mit der die Daten auf ihrem Weg durch das ausführende Programm verfolgt werden.
Summary
JavaScript is a programming language interpreted by all major web browsers. Owing to its widespread use, rich interfaces and somewhat lax protections, JavaScript is often exploited for attacks that breach the confidentiality and integrity of sensitive data in the browser - passwords, credit card numbers, cookies, etc. We report recent work on protecting JavaScript with information flow analysis, a technique that tracks data as it flows through an executing program. Our implementation is backed by a theoretical model and incurs only moderate performance overhead.

1. Einführung

Bis 1995 waren Websites im Internet lediglich in der Lage, Daten bereitzustellen – Text, Bilder und Videos. Diese Daten wurden mithilfe fester Rendering-Algorithmen auf den Bildschirmen der Anwender durch Webbrowser dargestellt. 1995 stellte Netscape die Web-Programmiersprache JavaScript vor. Seitdem stellen Websites ganz selbstverständlich nicht nur Daten bereit, sondern außerdem JavaScript-Programme, die durch den Webbrowser auf dem Rechner des Anwenders ausgeführt werden.

JavaScript ist nützlich. JavaScript-Programme ermöglichen interaktive Landkarten, Farbenspiele in Anzeigen, automatisch aufklappende Menüs und client-seitige Rechtschreibkontrollen – allesamt Funktionen, die heute gang und gäbe sind. Praktisch jeder interaktive, mit umfassender Funktionalität ausgestattete Content im modernen Internet basiert auf JavaScript-Programmen. Mehr als 95 % aller Websites enthalten JavaScript-Programme. Alle wichtigen Browser, wie Chrome, Internet Explorer, Firefox und Safari, können die Sprache umsetzen. Die einzelnen Browser liefern sich einen erbitterten Wettkampf um die beste JavaScript-Performance.

JavaScript ist sehr leistungsfähig (expressiv). JavaScript-Programme haben Zugriff auf eine breite Palette unterschiedlicher Browser-Schnittstellen. Sie können den im Webbrowser angezeigten Content auslesen und verändern, sie können Daten an beliebige Webserver senden und von dort empfangen, sie können auf gespeicherte Cookies zugreifen, und sie können weitere JavaScript-Programme aufrufen, die von beliebigen Webservern heruntergeladen wurden. Allerdings sind JavaScript-Programme normalerweise auf die Umgebung des Webbrowsers beschränkt; sie können also nicht auf andere Ressourcen des Rechners zugreifen, wie dessen Festplattenlaufwerk oder Arbeitsspeicher.

Aufgrund seiner Leistungsfähigkeit und weiten Verbreitung stellt JavaScript ein enormes Sicherheitsrisiko dar. Böswillige JavaScript-Programme können einen beträchtlichen Schaden anrichten – angefangen mit der Übernahme des Benutzer-Rechners bis zur Ausspähung der persönlichen Daten des Benutzers, wie Passwörter, Kreditkartennummern und Cookies. Darüber hinaus stammt nicht-vertrauenswürdiges JavaScript häufig aus vertrauenswürdigen Websites, sei es in Form von Code-Bibliotheken Dritter oder vom Website-Entwickler eingebundener externer Werbung – oder durch Hacker manipulierte Websites, die JavaScript-Schadsoftware bereitstellen. In solchen Fällen läuft das nicht-vertrauenswürdige JavaScript also im Browser, ausgestattet mit den der vertrauenswürdigen Website zugewiesenen Zugriffsrechten, was sogar noch mehr Gefahrenpotenzial birgt.

JavaScript-gestützte Angriffe lassen sich grob in zwei Kategorien unterteilen. Erstens kann ein JavaScript-Schadprogramm einen Programmfehler im Browser ausnutzen, um entweder den Rechner des Benutzers unter seine Kontrolle zu bringen oder sonstigen Schaden anzurichten. Auf ähnliche Weise funktionieren viele Viren und andere Schadsoftware. Zweitens kann ein Schadprogramm aufgrund der Expressivität von JavaScript und des sensiblen Contents, der den Browser durchläuft, auch ohne die Ausnutzung von Programmierfehlern der Implementierung Schaden anrichten. Hier einige Beispiele für Angriffe, die JavaScript-Programme in der zweiten Kategorie ausüben können:

  • Ausspähen der Benutzereingabe einer Kreditkartennummer oder eines Kennworts und Übermittlung zu einem Hacker-Webserver im Hintergrund.
  • Unbemerkte Manipulation der URL, auf die ein Hyperlink auf einer angezeigten Seite verweist. Klickt der Benutzer auf den Hyperlink, wird er zur neuen (möglicherweise gehackten oder schädlichen) URL umgeleitet.
  • Einblenden von Werbung auf dem nützlichen Teil einer Webseite, sodass der Benutzer gestört und behindert wird, während der Werbetreibende profitiert.

Allgemein gesehen, gründen sich die Angriffe der ersten Kategorie auf Programmfehler in der Implementierung des Browsers. Zu deren Abwehr müssen diese Fehler beseitigt werden, was in der Softwarebranche zur üblichen Vorgehensweise gehört. Die Angriffe der zweiten Kategorie sind dagegen möglich, weil die Angreifer eine bestimmte JavaScript-Funktion auf eine Weise nutzen, die von den Entwicklern nicht vorgesehen war. Diesen Problemen auf der Gestaltungsebene ist nur durch strengere Prinzipien der Abwehrmechanismen beizukommen. Dieser Artikel berichtet über eine kürzlich erfolgte Arbeit an einem Schutzmechanismus, der vor bestimmten Angriffen der zweiten Kategorie schützen soll. Zuvor aber sollen kurz einige bekannte Lösungsansätze erläutert werden, und warum diese nicht immer funktionieren.

2. Lösungen und Informationsflussanalyse

Wie können wir Abwehrmechanismen gestalten, um Angriffe der zweiten Kategorie zu verhindern? Eine naheliegende Methode wäre die Reduzierung der Browser-Schnittstellen, die den JavaScript-Programmen zur Verfügung stehen. Um beispielsweise den Angriff zu verhindern, der als erster Punkt im vorhergehenden Abschnitt genannt wurde, könnte der Browser so umgestaltet werden, dass JavaScript-Programme keine Passwort-Felder auslesen können. Unglücklicherweise sperrt eine Reduzierung der Schnittstellen aber auch nützliche Programme aus. So schließt beispielsweise die Sperrung des JavaScript-Zugriffs auf Passwort-Felder auch ein JavaScript-Programm aus, das lokal prüft, ob ein neues vom Benutzer eingegebenes Passwort ausreichend sicher ist (z. B. eine vorgegebene Mindestlänge hat). Die meisten Schnittstellen, die JavaScript-Programmen heute zur Verfügung stehen, sind aus der Notwendigkeit geboren, bestimmte nützliche Programme zu schreiben.

Alternativ kann ein Browser eine strenge Abtrennung erzwingen, das heißt, er lässt JavaScript von einer Domain (Website) nur auf Ressourcen aus derselben Domain zugreifen. Während dieses Konzept die Möglichkeit böswilliger Aktivitäten auf einer Website minimiert, die über die von den Entwicklern bereitgestellten Aktivitäten hinausgehen, behindert es in vielen Fällen die Funktionalität. Außerdem lässt es die Möglichkeit von Angriffen offen, die sich aus Nachlässigkeiten der Website-Programmierung ergeben. So könnte zum Beispiel ein wohlmeinender aber unerfahrener Entwickler eine externe JavaScript-Bibliothek mit Schadsoftware in der Website eingebunden haben. (Browser implementieren zwar eine strenge Trennung in der sogenannten Same-Origin Policy, aber JavaScript ist von dieser Richtlinie zumeist ausgenommen.)

Ein dritter Lösungsansatz, der in den letzten drei Jahren einige Untersuchungen auf sich gezogen hat, und zu dem unsere Arbeit einen Beitrag leisten soll, ist die Informationsflussanalyse. Die Idee besteht darin, den Browser so anzupassen, dass er verfolgt (Tracking), wohin jedes sensible Datenelement bei der Ausführung des JavaScripts fließt. Damit können Benutzer sowie die Entwickler vertrauenswürdiger Websites Richtlinien bereitstellen, die bestimmte Datenflüsse untersagen, und diese Richtlinien durch ein im Browser integriertes Überwachungsmodul umsetzen. Beispielsweise könnte bei dem ersten oben genannten Listenpunkt eine Richtlinie untersagen, dass der Inhalt eines Passwort-Felds auf irgendeinen Webserver gelangen darf, abgesehen von dem, der die Website hostet. Der Webbrowser kann diese Richtlinie umsetzen, indem er verfolgt, wohin die Passwort-Information fließt. Im Gegensatz zur Begrenzung der Schnittstellen oder der Erzwingung einer strikten Trennung ist die Informationsflussanalyse flexibel, abwärtskompatibel und wirkungsvoll (so lassen sich z. B. alle drei oben aufgeführten Angriffsvarianten mittels Informationsflussanalyse verhindern, die Lösung sperrt jedoch keine nützlichen Programme, wie die der Passwort-Längenüberprüfung).

In der kürzlich abgeschlossenen und durch das Schwerpunktprogramm RS3 der Deutschen Forschungsgemeinschaft finanzierten Arbeit haben Forscher am Max-Planck-Institut für Softwaresysteme gemeinsam mit der Universität Saarland ein vollständiges, praxistaugliches System zur Überwachung der Datenströme in JavaScript implementiert [1]. Die im weit verbreiteten Webbrowser Safari implementierte Lösung modifiziert den sogenannten Bytecode-Interpreter des Browsers, um die Daten zu verfolgen, während sie ein ausführendes Programm durchlaufen. Beim Bytecode handelt es sich um einen Zwischencode, in den jeder JavaScript zunächst kompiliert wird. Durch die Nutzung des Bytecodes bleiben die verschiedenen (über mehrere Jahre durch die Branche entwickelten) Leistungsoptimierungen erhalten. Darüber hinaus haben die Forscher ein theoretisches Modell des Interpreters erstellt und mathematisch nachgewiesen, dass die Implementierungsmethode korrekt ist (d. h. die Daten korrekt verfolgt werden). Die größte Herausforderung der Arbeit bestand darin, die fast 20.000 Zeilen undokumentierten Interpreter-Codes zu verstehen, anzupassen und zu modellieren. Hinzu kam die Beherrschung neuer Sprachkonstrukte, die in Bytecode auftreten, aber nicht im JavaScript selbst, wobei man sich der Hilfe durch Methoden der statischen Programmanalyse bediente.

Während viele Forscher verschiedenartige Methoden und Implementierungen zur Verfolgung der Datenströme in Teilen von JavaScript vorgestellt haben, ist die einzige andere Lösung für JavaScript in seiner Gesamtheit die von Hedin et al. [2]. Im Unterschied zu unserer Arbeit implementiert ihre Lösung einen völlig neuen JavaScript-Interpreter. Der praktische Vorteil unseres Ansatzes ist eine Verbesserung in etwa um Faktor 2, bezogen auf den Tracking-Overhead (je nach Benchmark variiert unser Overhead zwischen 28 und 42 %).

Literaturhinweise

Bichhawat, A.; Rajani, V.; Garg, D.; Hammer, C.
Information Flow Control in WebKit's JavaScript Bytecode
Principles of Security and Trust: Third International Conference, POST 2014, Held as Part of the European Joint Conferences on Theory and Practice of Software, ETAPS 2014, Grenoble, France, April 5-13, 2014, Proceedings. (Eds.) Abadi, M.; Kremer, S. Springer, Berlin 2014, pp. 159-178
Hedin, D.; Sabelfeld, A.
Information-flow security for a core of JavaScript
In: Proceedings of the 2012 IEEE 25th Computer Security Foundations Symposium (CSF '12). IEEE Computer Society, Washington DC 2012, pp. 3-18
Zur Redakteursansicht