Der größte Teil der Sicherheit einer Website steckt in ihrem Code, aber eine kleine Gruppe von HTTP-Antwort-Headern entscheidet, was der Browser mit diesem Code tun will, sobald er ankommt. Sie sind der Unterschied zwischen einem eingeschleusten <script>, das läuft, und einem, dessen Ausführung der Browser verweigert; zwischen einem ersten Besuch über unverschlüsseltes HTTP und einem, der schon in der Adresszeile auf HTTPS gehoben wird.
Zudem sind sie ungewöhnlich günstig auszurollen. Header sind Konfiguration, kein Code: die meisten sind eine Zeile in deinem Webserver, CDN oder Framework und gelten sofort für jede Antwort. Dieser Leitfaden behandelt, was jeder Header tut, welche sich rechnen, wie du eine Content-Security-Policy einführst, ohne deine eigene Website zu zerlegen, und wie du prüfst, was du heute sendest.
Warum Sicherheitsheader wichtig sind
Ein Header ist eine Anweisung an den Browser, und der Browser ist der Einzige, der sie auf dem Rechner des Nutzers durchsetzen kann. Damit sind Header eine zweite Verteidigungslinie, die auch dann hält, wenn vorher schon etwas schiefgegangen ist:
- Sie begrenzen Fehler, die du schon gemacht hast. Eine Content-Security-Policy behebt keine XSS-Lücke, kann aber verhindern, dass das eingeschleuste Skript läuft oder etwas zurücksendet.
- Sie schließen ganze Angriffsklassen. Clickjacking, MIME-Verwirrung und der Rückfall auf HTTP sind keine Fehler in deinem Code, sondern Browser-Verhalten, von dem du dich ausdrücklich abmelden musst.
- Sie stoppen stille Datenlecks. Ohne Referrer-Richtlinie reist die vollständige URL einer privaten Seite zu jedem Dritten, von dem du ein Bild lädst.
- Sie sind das Erste, worauf ein Prüfer schaut. Fehlende Header tauchen in jedem automatischen Scan, Sicherheitsfragebogen und Lieferanten-Review auf, weil sie der billigste mögliche Befund sind.
Der Haken: man bekommt sie leicht fast richtig. Eine CSP mit unsafe-inline blockiert sehr wenig; ein HSTS-Header mit einem Tag Laufzeit schützt fast niemanden. Der Wert liegt in den Details.
Die Header, die am meisten zählen
Sechs Header leisten nahezu die ganze nützliche Arbeit. Der Rest ist Altlast, sehr eng begrenzt oder überholt.
| Header | Was er verhindert | Aufwand |
|---|---|---|
Content-Security-Policy | XSS, Datenabfluss, unerwarteter Fremdcode | Hoch |
Strict-Transport-Security | Rückfall auf HTTP, Cookie-Diebstahl bei der ersten Anfrage | Gering |
X-Content-Type-Options | Dass eine hochgeladene Datei als Skript ausgeführt wird | Minimal |
Referrer-Policy | Vollständige URLs an Dritte zu verlieren | Minimal |
Permissions-Policy | Zugriff auf Kamera, Mikrofon und Standort | Gering |
Cross-Origin-Opener-Policy | Angriffe über Fenster, die du öffnest | Gering |
Fang unten an. Die vier günstigen Header sind je eine Zeile und zerlegen praktisch nie etwas, also lohnt es sich, sie heute auszuliefern; die CSP ist die, die einen Plan braucht.
Content-Security-Policy
Die CSP ist eine Erlaubnisliste, die dem Browser sagt, woher jede Art von Ressource kommen darf. Eine angemessen strenge Richtlinie sieht so aus:
default-src 'self'; script-src 'self' 'nonce-r4nd0m'; style-src 'self'; img-src 'self' data:; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; object-src 'none'
Lies sie Direktive für Direktive. default-src 'self' ist der Rückfall: alles, was nicht ausdrücklich genannt ist, darf nur von deiner eigenen Herkunft laden. script-src ist die wichtigste, denn Skripte sind das, was ein Angreifer ausführen will. frame-ancestors 'none' sagt, dass keine andere Website deine Seite in einen Rahmen setzen darf, und ist der moderne Ersatz für X-Frame-Options. object-src 'none' und base-uri 'self' schließen zwei alte Umwege, deren Abschalten nichts kostet.
Zwei Werte entscheiden, ob deine Richtlinie echt ist. 'unsafe-inline' in script-src erlaubt jedes eingebettete <script>, auch das von einem Angreifer eingeschleuste – genau das, was die CSP verhindern soll. 'unsafe-eval' erlaubt eval() und Verwandte. Eine Richtlinie mit einem von beiden lohnt sich für die übrigen Direktiven, schützt dich aber nicht vor XSS.
Der Ausweg ist ein Nonce oder ein Hash. Ein Nonce ist ein Zufallswert, der pro Antwort erzeugt und sowohl an den Header als auch an jedes legitime <script nonce="..."> gehängt wird; der Browser führt nur Skripte mit diesem Wert aus, und ein eingeschleustes Skript kann ihn nicht erraten. Hashes funktionieren genauso für Skripte, die sich nie ändern. Wenn dein Framework eingebettete Skripte rendert, unterstützt es Nonces fast sicher schon; das Einschalten ist meist eine Konfigurationsoption.
Strict-Transport-Security
HSTS sagt dem Browser, unverschlüsseltes HTTP für deine Domain über einen bestimmten Zeitraum zu verweigern. Das schließt die Lücke, die eine Weiterleitung allein offen lässt: die allererste Anfrage reist vor der Weiterleitung unverschlüsselt und kann abgefangen werden.
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
max-age steht in Sekunden; ein Jahr ist das übliche Ziel, und unter ein paar Monaten bringt es wenig. includeSubDomains erweitert die Regel auf jede Subdomain – das macht sie wirksam und sollte zuerst geprüft werden: jede Subdomain, die noch über HTTP läuft, bricht weg. preload beantragt die Aufnahme in die Liste, die Browser mitbringen, sodass selbst der erste Besuch geschützt ist.
Behandle HSTS als schwer rückholbar. Ein Browser, der eine Jahresrichtlinie gesehen hat, setzt sie ein Jahr durch, was auch immer du danach sendest. Rolle sie mit kurzem max-age aus, prüfe, dass nichts kaputt ist, und erhöhe erst dann. Und weil HSTS nur zählt, wenn das Zertifikat dahinter gültig ist, prüfe Zertifikat und Ablauf mit dem SSL Checker, bevor du dich auf eine lange Laufzeit festlegst.
Die kleineren Header
Diese vier sind je eine Zeile und selten strittig:
X-Content-Type-Options: nosniff– verhindert, dass der Browser den Typ einer Antwort aus dem Inhalt errät. Ohne ihn kann eine als.txthochgeladene Datei als Skript ausgeführt werden, wenn der Browser meint, sie sehe so aus.Referrer-Policy: strict-origin-when-cross-origin– sendet die vollständige URL an deine eigene Herkunft, nur die Herkunft an andere HTTPS-Seiten und nichts beim Rückfall auf HTTP. Das ist der moderne Standard und verhindert, dass private Pfade und Parameter an Dritte gelangen.Permissions-Policy: camera=(), microphone=(), geolocation=()– schaltet Gerätefunktionen ab, die deine Seite nicht nutzt, für deine Seite und für alles Eingebettete.Cross-Origin-Opener-Policy: same-origin– trennt die Fensterbeziehung zu Seiten, die du öffnest oder die dich öffnen. Das blockiert eine Familie von Angriffen über Fenster hinweg und ist Voraussetzung für manche Isolationsfunktionen des Browsers.
X-Frame-Options: DENY gehört für ältere Browser weiterhin auf die Liste, aber frame-ancestors in deiner CSP ersetzt ihn und ist ausdrucksstärker. Sende beide; sie widersprechen sich nicht.
Eine CSP sicher ausrollen
Eine aus dem Bauch geschriebene CSP blockiert etwas, das deine eigene Website braucht. Der Weg „zuerst berichten“ vermeidet das vollständig:
- Liefere die vier günstigen Header heute aus.
nosniff, eine Referrer-Richtlinie, eine Permissions-Richtlinie und HSTS mit kurzemmax-age. Keiner davon zerlegt normalerweise eine funktionierende Website. - Füge
Content-Security-Policy-Report-Onlyhinzu. Gleiche Syntax, aber der Browser meldet Verstöße nur, statt sie zu blockieren. Deine Website verhält sich genau wie vorher. - Sammle ein bis zwei Wochen Meldungen. Sie nennen jedes Fremdskript, jede Schrift, jeden Analytics-Endpunkt und jeden eingebetteten Handler, den du vergessen hattest. Diesen Schritt überspringen die meisten – und er ist der, der alles Weitere schmerzlos macht.
- Zieh an, bis die Meldungen verstummen. Ergänze die Quellen, die du wirklich brauchst, und entferne die, die du nicht brauchst. Widerstehe dem Impuls, eine Direktive auf
*zu weiten, um eine einzelne Meldung zum Schweigen zu bringen. - Schalte auf Durchsetzung. Benenne den Header in
Content-Security-Policyum. Behalte die Report-Only-Variante daneben, wenn du später eine strengere Richtlinie testen willst. - Entferne
unsafe-inlinezuletzt. Das ist die eigentliche Arbeit: eingebettete Skripte in Dateien verschieben oder mit Nonces versehen. Alles davor ist Vorbereitung; dieser Schritt stoppt XSS.
Ein praktischer Hinweis: Header gelten pro Antwort, setze sie also am Rand – CDN, Reverse Proxy oder Framework-Middleware – und nicht Seite für Seite. An einer Stelle zu setzen ist auch der einzige Weg, sicher zu sein, dass Fehlerseiten und Weiterleitungen sie ebenfalls tragen – und genau dort fehlen sie meist.
Header, die du nicht mehr senden musst
Manche Ratschläge sind schlecht gealtert, und einen toten Header zu senden ist nicht harmlos: er kostet Bytes und lässt eine Prüfung erledigt aussehen, obwohl sie es nicht ist.
X-XSS-Protection– die Browser-Filter, die er steuerte, sind weg. Er tut in keinem aktuellen Browser etwas, und der alte Wertmode=blockbrachte einst eigene Probleme. Weglassen und auf CSP setzen.Public-Key-Pins– HPKP wurde aus Browsern entfernt. Man sperrte sich leicht aus der eigenen Domain aus, deshalb wurde es aufgegeben.Expect-CT– Certificate Transparency wird inzwischen standardmäßig erzwungen, der Header ist also überflüssig.Feature-Policy– umbenannt inPermissions-Policy, mit anderer Syntax. Sende den neuen; behalte den alten nur, wenn du etwas Uraltes unterstützen musst.
Umgekehrt lohnt es sich, etwas zu entfernen, das du freiwillig verrätst: ein Server- oder X-Powered-By-Header mit der genauen Framework-Version sagt einem Angreifer, welche Exploits er zuerst versuchen soll. Keine Schwachstelle, aber es gibt keinen Grund, das zu veröffentlichen.
So prüfst du deine eigenen Header
Header sind öffentlich, die Konfiguration einer Website zu prüfen dauert also Sekunden:
curl -I https://example.com. EineHEAD-Anfrage gibt jeden Antwort-Header aus, ohne die Seite zu laden. Mit-Lprüfst du die Endadresse statt einer Weiterleitung.- Das Netzwerk-Panel des Browsers. Entwicklertools öffnen, neu laden, das Hauptdokument wählen und den Abschnitt der Antwort-Header lesen. Die Konsole protokolliert außerdem jeden CSP-Verstoß samt blockierender Direktive – der schnellste Weg, eine Richtlinie zu debuggen.
- Ein Prüfer, der das Ergebnis bewertet. Unser Security-Header-Check ruft eine URL ab und zeigt, welche Header vorhanden sind, welche fehlen und wo ein vorhandener Header schwächer ist, als er aussieht.
Prüfe mehr als deine Startseite. Header sitzen oft auf der Hauptanwendung, aber nicht auf Hosts für statische Dateien, Fehlerseiten, API-Subdomains oder der Weiterleitung von www auf die Hauptdomain. Prüfe dabei auch, ob die Weiterleitungskette selbst korrekt ist; die Referenz der HTTP-Statuscodes sagt, welcher Code zu einem dauerhaften Umzug gehört.
Um eine Richtlinie zu schreiben, ohne die Syntax auswendig zu kennen, starte mit unserem CSP-Generator, der auch Einstellungen markiert, die sie schwächen.
Häufige Fehler, die du vermeiden solltest
- Eine CSP mit
unsafe-inlineinscript-src. Sie besteht die meisten automatischen Prüfungen und stoppt fast kein XSS. Nutze einen Nonce oder Hashes. - Eine Direktive auf
*weiten, um eine Meldung zu beheben. Ergänze stattdessen die konkrete Herkunft;*gibt die Direktive ganz auf. - HSTS mit langem
max-ageam ersten Tag. Für die veröffentlichte Laufzeit praktisch unumkehrbar. Fang kurz an. includeSubDomainsvor der Prüfung der Subdomains. Alles, was noch auf HTTP läuft, auch interne Werkzeuge, lädt nicht mehr.- Header in der App, aber nicht am Rand. Fehlerseiten, Weiterleitungen und Datei-Hosts bleiben ungeschützt – und genau nach diesen Antworten greifen Angreifer.
- Denselben Header zweimal setzen. Zwei CSP-Header werden geschnitten, nicht zusammengeführt, das Ergebnis ist also strenger als jeder einzelne und zerlegt meist etwas; zwei widersprüchliche
X-Frame-Options-Werte können ganz ignoriert werden. - Weiterhin
X-XSS-Protectionsenden und es für erledigt halten. Ein toter Header erzeugt falsche Sicherheit in einem Bericht. - Nie über Report-Only hinauskommen. Meldungen, auf die du nicht reagierst, schützen niemanden.
Prüfe deine eigenen Header
Sicherheitsheader bieten das beste Verhältnis von Schutz zu Aufwand im Web: vier davon sind je eine Zeile, und der fünfte, die CSP, besteht vor allem darin, auf die eigenen Verstoßmeldungen zu hören, bevor man irgendetwas durchsetzt. Schau zuerst, was deine Website heute sendet, ergänze die günstigen Header sofort, und setze dann eine CSP im Report-Only-Modus ein und lass sie dir sagen, was deine Richtlinie enthalten muss.
Verwandte Tools und Leitfäden:
- Security-Header-Check – sieh, welche Header eine URL sendet und welche fehlen.
- SSL Checker – prüfe das Zertifikat hinter deiner HSTS-Richtlinie.
- HTTP-Statuscodes – stelle sicher, dass deine HTTPS-Weiterleitung den richtigen Code zurückgibt.
- DNS-Abfrage: der komplette Leitfaden – die Einträge hinter jedem Hostnamen in deiner Richtlinie.
Häufige Fragen
Welche Sicherheitsheader sollte ich zuerst ergänzen?⌄
Fang mit den vier an, die praktisch nie etwas zerlegen: X-Content-Type-Options: nosniff, Referrer-Policy: strict-origin-when-cross-origin, eine Permissions-Policy, die ungenutzte Gerätefunktionen abschaltet, und Strict-Transport-Security mit kurzem max-age, das du später erhöhst. Ergänze dann eine Content-Security-Policy im Report-Only-Modus, denn die braucht Beobachtung, bevor sie durchgesetzt wird.
Hilft eine CSP mit unsafe-inline noch?⌄
Sie hilft bei allem außer bei dem Angriff, den die CSP am besten stoppt. Direktiven wie img-src, connect-src, frame-ancestors und object-src begrenzen weiterhin, wohin Daten gehen und wer dich einrahmen darf. Aber 'unsafe-inline' in script-src erlaubt jedem eingeschleusten eingebetteten Skript zu laufen, die Richtlinie schützt dich also nicht vor XSS. Ersetze es durch einen Nonce pro Antwort oder durch Skript-Hashes.
Ist Strict-Transport-Security riskant?⌄
Riskant vor allem, weil es schwer rückgängig zu machen ist: ein Browser, der max-age=31536000 gesehen hat, verweigert unverschlüsseltes HTTP für deine Domain ein Jahr lang, egal was du danach sendest. Rolle es zuerst mit kurzem max-age aus, prüfe, dass jede Subdomain über HTTPS erreichbar ist, bevor du includeSubDomains ergänzt, und erhöhe erst dann die Laufzeit und erwäge preload.
Was hat X-Frame-Options und X-XSS-Protection ersetzt?⌄
Die Direktive frame-ancestors in der Content-Security-Policy ersetzt X-Frame-Options und ist ausdrucksstärker, wobei beide zu senden harmlos ist und älteren Browsern weiter hilft. X-XSS-Protection hat keinen Ersatz, weil die Browser-Filter, die er steuerte, entfernt wurden; XSS stoppt heute die CSP, und der alte Header kann einfach weggelassen werden.
Wo sollte ich Sicherheitsheader setzen?⌄
Am Rand, an einer Stelle: in deinem CDN, Reverse Proxy oder der Framework-Middleware. Setzt man sie pro Seite, bleiben Fehlerseiten, Weiterleitungen, Datei-Hosts und API-Antworten unabgedeckt – und genau diese Antworten werden bei einer Prüfung übersehen und bei einem Angriff gewählt.
Wie prüfe ich, welche Header meine Website sendet?⌄
Führe curl -I https://deinedomain.de aus und ergänze -L, damit du das Endziel statt einer Weiterleitung siehst, oder öffne das Netzwerk-Panel des Browsers und lies die Antwort-Header des Hauptdokuments. Der Security-Header-Check macht es aus deinem Browser und markiert sowohl fehlende Header als auch vorhandene, die schwächer sind, als sie aussehen.
Über 15 Jahre praktische Erfahrung in Webentwicklung und Sicherheit. Schwerpunkt ist praxisnahe Websicherheit: Websites sicher, zuverlässig und leicht zu diagnostizieren halten. BuildToolForge macht diese Prüfungen kostenlos, privat und verständlich. Die Tools laufen, wo immer möglich, in deinem Browser, und nichts, was du in ein Tool eingibst, wird gespeichert.
So schreiben wir unsere Anleitungen: Jede Anleitung entsteht aus praktischer Erfahrung, wird mit den offiziellen Standards abgeglichen und mit unseren eigenen Tools an echten Domains getestet. Wir aktualisieren die Anleitungen, wenn sich Standards oder Tools ändern. Einen Fehler gefunden? Schreib uns an contact@buildtoolforge.com.