CSP, SameSite & Co: was die kleinen Response-Header tatsächlich leisten
Eine Content Security Policy stoppt den Großteil der klassischen XSS- und Datenexfiltrations-Angriffe. SameSite-Cookies entschärfen CSRF. Richtig konfiguriert bilden sie eine zweite Verteidigungsschicht, die nur dann sichtbar wird, wenn die erste versagt. Wie eine strikte, aber praxistaugliche Header-Konfiguration aussieht.
Inhalt dieses Beitrags
- Problem
- Kurze Antwort
- Tiefgang
- CSP: die harte Schicht gegen XSS
- SameSite: CSRF-Schutz ohne eigenes Token
- Die begleitenden Header
- Die Rolle des Cookies mit Secure und HttpOnly
- Abgelehnte Alternativen und Mythen
- Was Sie jetzt tun sollten
- Wie Dernium hier hilft
- Verifikation
- Offene Punkte
- Häufige Fragen
- Reicht eine CSP allein, um XSS zu verhindern?
- Brauche ich noch ein CSRF-Token, wenn meine Cookies SameSite=Lax sind?
- Was ist der Unterschied zwischen SameSite=Lax und SameSite=Strict?
- Wie führe ich eine strikte CSP ein, ohne die Seite zu zerschießen?
- Was bringt der HSTS-Preload-Eintrag zusätzlich zu normalem HSTS?
Problem
Die meisten Angriffe auf authentifizierte Nutzer laufen nicht gegen den Anmelde-Mechanismus, sondern gegen den schon angemeldeten Browser. Cross-Site Scripting (XSS, das Einschleusen von fremdem JavaScript in eine vertrauenswürdige Seite) schleust fremdes JavaScript in die Seite; Cross-Site Request Forgery (CSRF, das Auslösen einer Aktion im Namen eines angemeldeten Nutzers von einer fremden Seite aus) nutzt das existente Session-Cookie (die kleine Kennung, an der der Server eine angemeldete Sitzung erkennt), um beliebige Aktionen auszulösen; Clickjacking legt eine transparente Seite über die echte und lässt den Nutzer versehentlich auf das Falsche klicken.
Alle drei Angriffsklassen sind lange bekannt. Sie leben nicht, weil sich Entwickler nicht darum kümmern, sondern weil die Schutzmechanismen viele kleine Details haben und ein einzelner Fehler die ganze Wirkung auflösen kann. Ein Beispiel: eine sonst saubere CSP mit einem einzigen unsafe-inline im script-src hebt den XSS-Schutz praktisch komplett aus.
Für wen ist das? Für Webentwickler und Betreiber, die ihre Anwendungen härten wollen.
Kurze Antwort
Drei Header-Familien (Gruppen von HTTP-Kopfzeilen, die der Server zu jeder Antwort mitschickt) leisten zusammen einen Großteil der Arbeit. Sie sind die zweite Schicht, die dann wirkt, wenn die erste (saubere Eingabe-Prüfung und Ausgabe-Maskierung im Code) versagt:
- Content Security Policy (CSP, eine Regelliste, welche Ressourcen die Seite laden darf): macht eine injizierte
<script>-Tag-Instanz wirkungslos, wenn sie nonce-basiert und ohneunsafe-inlinekonfiguriert ist. - Cookie-Attribute SameSite und Secure: verhindern, dass Session-Cookies bei Cross-Site-Requests (Anfragen, die von einer fremden Seite ausgehen) automatisch mitgehen; damit schlagen CSRF-Angriffe ins Leere.
- X-Content-Type-Options, Referrer-Policy, X-Frame-Options/frame-ancestors: decken Clickjacking, MIME-Sniffing-Angriffe (das eigenmächtige Umdeuten eines Dateityps durch den Browser) und Referer-Leaks ab.
- Strict-Transport-Security (HSTS): erzwingt HTTPS und schließt das Klartext-Fenster beim ersten Verbindungsaufbau.
- Secure und HttpOnly am Session-Cookie: schützen das Cookie vor Klartext-Leck und vor Auslesen durch JavaScript.
Kein einzelner Header ersetzt saubere Applikationshygiene. Zusammen bilden sie die Defense-in-Depth (mehrere unabhängige Schutzschichten hintereinander).
Wieso sind diese Sicherheitsfunktionen nicht einfach immer und überall aktiv? Das liegt daran, dass sich sehr viele Webseiten nicht mit diesen Themen auseinandersetzen und z.B. ohne unsafe-inline schlichtweg nicht mehr funktionieren würden.
Tiefgang
CSP: die harte Schicht gegen XSS
Ein CSP-Header listet erlaubte Herkünfte pro Ressourcentyp auf. Ein produktionsreifes Minimum:
Content-Security-Policy:
default-src 'none';
script-src 'nonce-abc123' 'strict-dynamic';
style-src 'self';
img-src 'self' data:;
connect-src 'self';
font-src 'self';
base-uri 'none';
form-action 'self';
frame-ancestors 'none';
upgrade-insecure-requestsDie wichtigsten Direktiven:
default-src 'none'startet explizit ohne Erlaubnis. Alles, was nicht weiter unten explizit freigegeben wird, ist verboten.script-src 'nonce-abc123' 'strict-dynamic': JavaScript läuft nur, wenn der script-Tag einen Nonce (eine pro Antwort neu erzeugte Einmal-Zufallszahl) mit passendem Wert trägt. Der Nonce ändert sich pro Response und wird serverseitig in die HTML-Seite injiziert. Ein injizierter Script-Tag ohne diesen Nonce wird vom Browser ignoriert.strict-dynamiclässt dynamisch per JavaScript eingefügte Scripts zu, solange ein nonce-valider Script sie erzeugt hat.style-src 'self': Styles nur aus der eigenen Origin (der eigenen Schema-Host-Port-Kombination). Wer inline-Styles braucht, nutzt'nonce-…'(selbes Prinzip), nichtunsafe-inline.base-uri 'none'verhindert, dass ein injiziertes<base>-Tag relative URLs umleitet.form-action 'self'verhindert, dass ein Formular nach extern posten kann.frame-ancestors 'none'ersetztX-Frame-Options: DENYund verhindert Clickjacking.upgrade-insecure-requests: HTTP-Ressourcen werden automatisch auf HTTPS umgeschrieben.
Was hier bewusst fehlt: 'unsafe-inline' und 'unsafe-eval'. Beide machen CSP effektiv wirkungslos gegen XSS. Legacy-Anwendungen, die sie benötigen, sollten in einer Übergangsphase mit Content-Security-Policy-Report-Only (ein Beobachtungsmodus, der Verstöße meldet, aber nichts blockt) messen, was geblockt würde, und die Codebasis stückweise auf Nonce-fähig oder Hash-fähig umstellen.
SameSite: CSRF-Schutz ohne eigenes Token
Cookies mit SameSite=Strict werden vom Browser bei Cross-Site-Requests (also Requests, deren Absender-Site sich von der Ziel-Site unterscheidet) nicht mitgeschickt. Ein Angreifer, der eine bösartige Seite auf evil.example.org hostet und von dort einen POST auf bank.example.de auslöst, bekommt keine Session-Cookies mehr mitgegeben, der Angriff scheitert an fehlender Authentifizierung.
Die Ausprägungen:
- Strict: Cookie wird nur bei same-site Requests mitgeschickt, inklusive Navigation. Stärkster Schutz. Nebenwirkung: wer auf einen Login-Link in einer E-Mail klickt, ist auf der Ziel-Seite nicht eingeloggt, weil die Navigation als cross-site gilt.
- Lax (seit 2020 Browser-Default): Cookie wird bei same-site Requests und bei Top-Level-Navigation (GET-Requests) mitgeschickt, aber nicht bei cross-site POST/PUT/DELETE. Guter Kompromiss für die meisten Anwendungen.
- None: Cookie wird immer mitgeschickt; nur zulässig in Kombination mit
Secure. Sollte bewusst eingesetzt werden, niemals als Default.
Produktive Empfehlung: SameSite=Lax für Session-Cookies in typischen Web-Anwendungen, SameSite=Strict für hochschutzbedürftige Anwendungen mit eigenen Login-Pfaden, die keine Deep-Links aus Mails erwarten.
Die begleitenden Header
Strict-Transport-Security (HSTS, HTTP Strict Transport Security): erzwingt HTTPS für die Domain. Nach dem ersten Besuch bleibt HSTS im Browser gepinnt; spätere HTTP-URLs werden automatisch nach HTTPS umgeschrieben, bevor sie den Netzwerk-Stack erreichen.
Strict-Transport-Security: max-age=63072000; includeSubDomains; preloadWer preload setzt und die Domain bei hstspreload.org registriert, wird in der HSTS-Liste des Browsers mitausgeliefert, ab dann ist auch der allererste Besuch pro HTTPS abgesichert.
X-Content-Type-Options: nosniff schaltet den Browser-eigenen MIME-Sniffer ab. Der Sniffer kann ein hochgeladenes Dokument als HTML interpretieren, selbst wenn der Server es als text/plain ausliefert, und damit eingebettetes JavaScript ausführen. Mit nosniff hält sich der Browser an den Content-Type.
Referrer-Policy: steuert, welcher Teil der aktuellen URL beim Verlassen der Seite im Referer-Header (die Kopfzeile, die der herkommenden Seite verrät, von wo der Nutzer kam) an die Zielseite geht. no-referrer ist die konservativste Wahl; strict-origin-when-cross-origin ein praxistauglicher Kompromiss.
Permissions-Policy (früher Feature-Policy): schaltet Browser-APIs (Schnittstellen wie Kamera- oder Mikrofon-Zugriff) für die eigene Seite und für eingebettete Frames ab. Wer keine Kamera, kein Mikrofon, keinen Fullscreen-Modus braucht, schaltet sie ab, und verkleinert die Angriffsfläche für spätere API-Lücken.
Cross-Origin-Opener-Policy: same-origin trennt die Window-Group der Seite von Popup-Fenstern und verhindert, dass der Popup-Opener Cross-Origin-Zugriffe aufmacht.
Cross-Origin-Resource-Policy: same-origin erlaubt den Resource-Embed nur für die eigene Origin. Verhindert, dass die Seite in einem fremden Kontext als Ressource eingebunden wird.
Cross-Origin-Embedder-Policy: require-corp schaltet Spectre-artige Timing-Angriffe (das Ausspähen von Speicher über minimale Laufzeitunterschiede) auf SharedArrayBuffer und High-Resolution-Timer ab, nur sinnvoll, wenn die Seite selbst diese APIs nutzt.
Die Rolle des Cookies mit Secure und HttpOnly
Neben SameSite gehören zwei weitere Attribute in jeden Session-Cookie:
Secure: Cookie wird nur über HTTPS-Verbindungen mitgeschickt. Fehlt das Attribut, leckt ein einziger HTTP-Redirect (durch einen Coffee-Shop-Hotspot, einen Proxy-Fehler, einen falschen Link) das Cookie im Klartext.HttpOnly: JavaScript in der Seite hat keinen Zugriff auf das Cookie. Damit bleibt das Cookie auch dann geschützt, wenn eine XSS-Lücke existiert, der Angreifer kann keinen Cookie-Wert ablesen.
Ein gutes Session-Cookie sieht z.B. so aus:
Set-Cookie: session=abc; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=7200Abgelehnte Alternativen und Mythen
"CSP mit unsafe-inline ist ja auch etwas." Nicht in der Praxis. Der XSS-Schutz steht und fällt mit dem Ausschluss von unsafe-inline. Wer unsafe-inline braucht, hat eine zu lösende Aufgabe. Es ist explizit nicht als konfigurierbarer Schalter gedacht.
"Ein CSRF-Token ist die einzige saubere Lösung." Anti-CSRF-Tokens bleiben wertvoll für formular-basierte Anwendungen, aber SameSite-Cookies decken einen Großteil der CSRF-Fälle bereits auf der Cookie-Schicht ab. Beide Schichten sind kompatibel; die Kombination ist Defense-in-Depth.
"X-XSS-Protection schützt uns." Der Header ist historisch; moderne Browser (Chrome, Firefox) haben die zugrundeliegende XSS-Filter-Logik abgeschaltet. CSP ist der aktuelle Schutz.
"HSTS brauchen wir nicht, wir redirecten per 301." Ein 301-Redirect bedeutet, dass der erste Request bereits HTTP-Klartext ist, und genau dort liegt das Angriffsfenster. HSTS schließt es ab dem zweiten Besuch; preload schließt es schon für den ersten.
Was Sie jetzt tun sollten
- Lassen Sie Ihre aktuelle Header-Konfiguration durch den CSP-Evaluator und das Mozilla Observatory laufen (Links unten). Das zeigt in Minuten, wo die größten Lücken sind.
- Prüfen Sie jeden Session-Cookie auf
HttpOnly,Secureund ein gesetztesSameSite(mindestensLax). Das ist die kleinste Änderung mit dem größten Effekt gegen CSRF und Cookie-Diebstahl. - Führen Sie eine CSP zunächst im
Content-Security-Policy-Report-Only-Modus ein, sammeln Sie ein bis zwei Wochen Verstöße, und schalten Sie sie erst dann scharf. So bricht nichts überraschend. - Setzen Sie HSTS mit langem
max-ageund beantragen Sie nach einer Testphase den Preload-Eintrag, damit auch der erste Besuch abgesichert ist. - Schalten Sie ungenutzte Browser-APIs per
Permissions-Policyab und ergänzen Sieframe-ancestors 'none'(bzw. eine explizite Allowlist), falls Ihre Seite nicht eingebettet werden soll.
Wie Dernium hier hilft
Alle Dernium-Webanwendungen fahren die hier beschriebene Header-Konfiguration als Baseline: strikte nonce-basierte CSP ohne unsafe-inline, SameSite-Lax-Session-Cookies mit Secure und HttpOnly, HSTS mit Preload-Eintrag, vollständige X-Content-Type-Options, Referrer-Policy und Permissions-Policy. Dernium Upload ist hier besonders relevant: das Widget läuft als eingebundenes Element in fremden Seiten, die wiederum eigene CSPs mitbringen; unsere Header-Konfiguration sorgt dafür, dass das Widget auch in weich konfigurierten Host-Seiten nicht zum Angriffsvektor wird. Ihre eigene Content-Security-Policy können Sie vorab gegenprüfen: Unter den kostenlosen Webtools finden Sie einen CSP-Header-Validator und einen Generator, die typische Lücken wie ein fehlendes object-src direkt benennen.
Verifikation
- CSP-Evaluator (Google): csp-evaluator.withgoogle.com, prüft eine CSP-Konfiguration auf typische Schwächen.
- Mozilla Observatory: observatory.mozilla.org, aggregierte Einschätzung aller Security-Header einer Seite.
- Hardenize: hardenize.com, ausführliche Analyse inkl. E-Mail- und DNS-Aspekten.
- HSTS-Preload-Eintragung: hstspreload.org.
- MDN-Dokumentation:
Content-Security-Policy,Set-Cookie,Strict-Transport-Security.
Offene Punkte
CSP-Reporting. report-uri oder das modernere report-to liefern Violations an einen Endpoint zur Auswertung. In Produktion werden regelmäßig Violations aus Browser-Extensions, Werbefiltern oder seltenen Browser-Bugs gemeldet, die nicht auf echte Angriffe hindeuten. Das Rauschen muss gefiltert werden, sonst verliert der Report-Stream seinen Nutzen.
Browser-Unterschiede. Einige Direktiven (prefetch-src, report-to) sind nicht in allen Browsern stabil. Wer breit ausliefert, testet in Chromium, Firefox und Safari.
Legacy-Anwendungen. Alte Codebasen mit inline-Event-Handlern (onclick="…") sind mit einer strikten CSP inkompatibel. Die Umstellung auf addEventListener-Style ist Aufwand, zahlt sich aber sowohl für CSP als auch für Code-Qualität aus. Für Zwischenschritte hilft Content-Security-Policy-Report-Only, die geplante Policy ohne Wirkung zu beobachten, bevor sie scharf geschaltet wird.
Häufige Fragen
Reicht eine CSP allein, um XSS zu verhindern?
Nein. Eine CSP ist die zweite Verteidigungsschicht, die greift, wenn die erste versagt. Die erste Schicht bleibt sauberes Maskieren aller Ausgaben und Prüfen aller Eingaben im Anwendungscode. Eine CSP fängt das ab, was trotzdem durchrutscht, und nur dann zuverlässig, wenn sie ohne unsafe-inline und mit Nonce oder Hash konfiguriert ist.
Brauche ich noch ein CSRF-Token, wenn meine Cookies SameSite=Lax sind?
In vielen Fällen deckt SameSite=Lax die gängigen CSRF-Wege bereits ab. Für formular-basierte Anwendungen und für besonders schutzbedürftige Aktionen ist ein zusätzliches Anti-CSRF-Token aber weiterhin empfehlenswert, weil es unabhängig vom Cookie-Verhalten des Browsers wirkt. Beide Mechanismen zusammen sind robuster als jeder für sich.
Was ist der Unterschied zwischen SameSite=Lax und SameSite=Strict?
Lax schickt das Cookie bei normaler Navigation (etwa beim Klick auf einen Link von außen) mit, aber nicht bei schreibenden Cross-Site-Anfragen wie POST. Strict schickt das Cookie bei jeder Anfrage von einer fremden Seite gar nicht mit, auch nicht beim Klick auf einen Login-Link aus einer E-Mail. Strict ist sicherer, aber unbequemer; Lax ist für die meisten Anwendungen der richtige Kompromiss.
Wie führe ich eine strikte CSP ein, ohne die Seite zu zerschießen?
Über den Beobachtungsmodus Content-Security-Policy-Report-Only. Damit meldet der Browser alle Verstöße an einen Endpoint, blockt aber nichts. Sie sammeln ein bis zwei Wochen lang, beheben die gemeldeten Fälle (etwa inline-Scripts auf Nonce umstellen) und schalten die Policy erst dann mit dem scharfen Content-Security-Policy-Header live.
Was bringt der HSTS-Preload-Eintrag zusätzlich zu normalem HSTS?
Normales HSTS wirkt erst ab dem zweiten Besuch, weil der Browser die Regel erst beim ersten erfolgreichen HTTPS-Besuch lernt. Genau der erste Request kann aber noch über HTTP-Klartext laufen und abgefangen werden. Mit einem Preload-Eintrag ist Ihre Domain fest in der Liste der Browser hinterlegt, sodass schon der allererste Verbindungsversuch zwingend über HTTPS geht.