Sichere Programmierung für JavaScript-Entwickler
JavaScript läuft überall dort, wo ein Browser läuft, was es zu einer der am häufigsten angegriffenen Angriffsflächen in der Softwareentwicklung macht. Die meisten Schwachstellen stammen nicht von exotischen Exploits – sie entstehen durch vorhersehbare Muster in alltäglichem Code. Dieser Leitfaden behandelt die wichtigsten Praktiken für sichere JavaScript-Programmierung, wenn Ihr Code direkt im Browser ausgeführt wird.
Wichtigste Erkenntnisse
- DOM-basiertes XSS ist eine der häufigsten JavaScript-Schwachstellen – vermeiden Sie es, nicht vertrauenswürdige Daten an Senken wie
innerHTML,eval()oderdocument.write()zu übergeben - Verwenden Sie
textContentanstelle voninnerHTMLfür benutzergenerierte Daten und bereinigen Sie mit DOMPurify, wenn Sie tatsächlich HTML rendern müssen - Verwenden Sie niemals
eval(),Function()oder string-basiertesetTimeout/setIntervalmit dynamischen Eingaben - Setzen Sie Content Security Policy (CSP) mit Nonces oder Hashes durch und kombinieren Sie sie mit Trusted Types für mehrschichtige Verteidigung
- Vermeiden Sie die Speicherung von Authentifizierungs-Tokens oder Geheimnissen in
localStorageodersessionStorage– bevorzugen SieHttpOnly-Cookies für Session-Identifier - Validieren Sie immer
event.originbei der Verarbeitung vonpostMessage-Events - Überprüfen Sie regelmäßig Ihren Dependency-Tree und verwenden Sie Subresource Integrity für CDN-geladene Skripte
DOM-basiertes XSS: Eine der häufigsten JavaScript-Schwachstellen
Die Verhinderung von XSS in JavaScript beginnt mit dem Verständnis, wo es tatsächlich entsteht. DOM-basiertes XSS tritt auf, wenn Ihr Code aus einer vom Angreifer kontrollierten Quelle liest – wie location.hash, document.referrer oder URLSearchParams – und diese unsicher in die Seite schreibt.
Die Grundregel: Übergeben Sie niemals nicht vertrauenswürdige Daten an eine Senke, die HTML interpretiert oder Code ausführt.
Unsichere Senken, die mit dynamischen Daten vermieden werden sollten:
// ❌ Alle diese können injizierte Skripte ausführen
element.innerHTML = userInput
element.outerHTML = userInput
document.write(userInput)
eval(userInput)
setTimeout(userInput, 0) // nur String-Form
new Function(userInput)()
Sichere Alternativen:
// ✅ Behandelt Inhalte als Text, niemals als Markup
element.textContent = userInput
element.innerText = userInput
Wenn Sie tatsächlich HTML rendern müssen – beispielsweise bei einem Rich-Text-Editor – bereinigen Sie zuerst mit DOMPurify:
// ✅ Sicheres Rich-Text-Rendering
element.innerHTML = DOMPurify.sanitize(userInput)
Die gleiche Logik gilt für setAttribute. Dynamische Attribute wie href, src und Event-Handler (onclick, onload) können JavaScript ausführen. Bleiben Sie bei statischen, nicht ausführbaren Attributen, wenn Sie mit benutzergenerierten Werten arbeiten.
Dynamische Code-Ausführung ist immer unsicher
eval(), Function() und string-basierte setTimeout/setInterval sind nicht nur schlechte Praxis – sie sind direkte Code-Injection-Vektoren. Es gibt keine sichere Möglichkeit, sie mit nicht vertrauenswürdigen Eingaben zu verwenden.
Wenn Sie Daten parsen, verwenden Sie JSON.parse(). Wenn Sie dynamisches Verhalten benötigen, nutzen Sie Datenstrukturen und explizite Logik anstelle von Laufzeit-Code-Generierung.
Content Security Policy als Verteidigungsschicht
Content Security Policy (CSP) begrenzt, welche Skripte ausgeführt werden können und woher sie geladen werden dürfen. Eine strikte CSP mit Nonces oder Hashes – anstelle von 'unsafe-inline' – reduziert den Schadensradius jedes XSS, das durchrutscht, erheblich.
Kombinieren Sie CSP mit Trusted Types in unterstützten Browsern, um sichere DOM-Schreibvorgänge auf API-Ebene durchzusetzen. Die Browser-Unterstützung für Trusted Types wird kontinuierlich erweitert und kann auf webstatus.dev verfolgt werden.
Discover how at OpenReplay.com.
Sichere Browser-APIs: Cookies und Client-seitige Speicherung
Cookies, die Session-Identifier enthalten, sollten immer HttpOnly (blockiert JavaScript-Zugriff), Secure (nur HTTPS) und SameSite=Strict oder Lax (hilft bei der CSRF-Abwehr) tragen. Das serverseitige Setzen dieser Attribute ist zuverlässiger als über JavaScript.
localStorage und sessionStorage sind für jedes Skript auf der Seite zugänglich. Vermeiden Sie die Speicherung von Authentifizierungs-Tokens, Session-Geheimnissen oder sensiblen Benutzerdaten dort – eine XSS-Schwachstelle legt sofort alles im Storage offen.
Cross-Origin-Messaging mit postMessage
postMessage ist nützlich, aber leicht falsch zu verwenden. Validieren Sie immer den origin eingehender Nachrichten, bevor Sie auf deren Daten reagieren:
window.addEventListener('message', (event) => {
// ✅ Immer Origin validieren vor der Verarbeitung
if (event.origin !== 'https://trusted-origin.com') return
handleMessage(event.data)
})
Vermeiden Sie beim Senden von Nachrichten die Verwendung von '*' als targetOrigin, es sei denn, Sie haben wirklich kein festes Ziel. Auf der Empfängerseite validieren Sie immer event.origin, um sicherzustellen, dass die Nachricht von einer vertrauenswürdigen Site stammt. Weitere Details zur sicheren Verwendung finden Sie in der postMessage-Dokumentation.
JavaScript Supply Chain Security
Die Sicherheit der JavaScript-Lieferkette ist ein wachsendes Anliegen. Ein einzelnes kompromittiertes oder bösartiges Paket kann Tausende von Anwendungen betreffen. Praktische Schritte:
- Führen Sie
npm auditaus oder verwenden Sie Snyk, um bekannte Schwachstellen in Abhängigkeiten zu erkennen - Committen Sie Ihre Lockfile (
package-lock.jsonoderyarn.lock) und behandeln Sie unerwartete Änderungen als Warnsignal - Verwenden Sie Subresource Integrity (SRI)-Hashes für alle von einem CDN geladenen Skripte
- Prüfen Sie neue Pakete vor dem Hinzufügen – überprüfen Sie Download-Zahlen, Wartungsaktivität und ob der Paketname ein Typosquat sein könnte
Schnellreferenz: Sichere vs. unsichere Muster
| Unsicher | Sichere Alternative |
|---|---|
innerHTML = userInput | textContent = userInput |
eval(str) | JSON.parse(str) |
setTimeout(str, n) | setTimeout(fn, n) |
Token in localStorage | HttpOnly-Cookie |
message ohne Origin-Prüfung | event.origin zuerst validieren |
Fazit
Die meisten JavaScript-Schwachstellen folgen demselben Muster: Nicht vertrauenswürdige Daten erreichen eine mächtige API ohne Validierung. Gewöhnen Sie sich an, die Frage zu stellen: „Woher kommt dieser Wert und was kann diese API damit tun?” Diese Frage, konsequent angewendet, fängt die Mehrheit der Probleme ab, bevor sie in Produktion gehen.
FAQs
Nicht immer, aber es ist unsicher, wenn Sie Daten übergeben, die von Benutzereingaben oder einer externen Quelle stammen. Wenn Sie dynamisches HTML rendern müssen, bereinigen Sie es zuerst mit einer Bibliothek wie DOMPurify. Für reinen Textinhalt verwenden Sie textContent, das niemals Markup interpretiert oder Skripte ausführt.
localStorage ist für jedes JavaScript zugänglich, das auf der Seite läuft. Wenn ein Angreifer eine XSS-Schwachstelle ausnutzt, kann er alles im Storage lesen, einschließlich Ihrer Tokens. HttpOnly-Cookies sind eine sicherere Wahl, da JavaScript überhaupt nicht darauf zugreifen kann, was den Schaden durch clientseitige Angriffe begrenzt.
Reflected XSS beinhaltet eine bösartige Payload, die an den Server gesendet und in der Antwort zurückgegeben wird. DOM-basiertes XSS erreicht den Server nie. Stattdessen liest clientseitiges JavaScript aus einer vom Angreifer kontrollierten Quelle wie dem URL-Fragment und schreibt es unsicher in die Seite. Beide sind gefährlich, aber DOM-basiertes XSS ist serverseitig schwerer zu erkennen.
CSP teilt dem Browser mit, welche Skript-Quellen ausgeführt werden dürfen. Eine strikte Policy mit Nonces oder Hashes blockiert Inline-Skripte und nicht autorisierte externe Skripte. Selbst wenn ein Angreifer bösartiges Markup in die Seite injiziert, weigert sich der Browser, es auszuführen, weil es nicht mit der Policy übereinstimmt.
Complete picture for complete understanding
Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.
Check our GitHub repo and join the thousands of developers in our community.