WordPress Security-Header: CSP, HSTS & Co. richtig setzen
WordPress Security-Header erklärt: sichere Startwerte für HSTS, CSP, Referrer-Policy und Permissions-Policy, wo Sie sie setzen und wie nichts kaputtgeht.
Auch auf: English Français Español Italiano Polski Português Türkçe
Eine schlechte Header-Bewertung lässt sich bei den meisten WordPress-Websites in weniger als einer Stunde beheben. Vier risikoarme Header können Sie noch heute setzen: HSTS, X-Content-Type-Options, Referrer-Policy und Permissions-Policy. Die Content-Security-Policy führen Sie danach schrittweise ein, zuerst im Report-Only-Modus. Dieser Leitfaden zeigt sichere Startwerte für diese WordPress Security-Header, erklärt, wo Sie sie konfigurieren, und sagt Ihnen, welche davon den Editor oder eingebettete Inhalte lahmlegen können.
Was WordPress Security-Header leisten und was nicht
Security-Header sind Anweisungen, die Ihr Server an den Browser des Besuchers schickt. Sie legen fest, dass der Browser nur HTTPS nutzt, keine Dateitypen errät, Ihre Seiten nicht in fremde Frames einbetten lässt und nur bestimmte Skripte ausführt. Das erschwert Downgrade-Angriffe, MIME-Sniffing und Clickjacking und begrenzt die Folgen von Cross-Site-Scripting (XSS).
Eine verwundbare Erweiterung patchen sie nicht. Sie stoppen auch keine Passwort-Rateangriffe und schützen den Server selbst nicht. Kann ein Angreifer PHP-Dateien auf Ihren Server schreiben, hilft Ihnen kein Header der Welt. Betrachten Sie Header als eine Schicht der browserseitigen Härtung, nicht als Behebung einer Sicherheitslücke.
Fehlende Header sind ein typisches Beispiel für Sicherheitsfehlkonfiguration aus den OWASP Top 10. Gleichzeitig sind sie günstig und gut dokumentiert. Damit gehören sie zum „Stand der Technik“, den Sie nach Art. 32 DSGVO berücksichtigen müssen.
Die Basis: sichere Startwerte für jeden Header
Die folgenden Werte sind bewusst zurückhaltend gewählt. Eine typische WordPress-Website sollte damit weiterhin funktionieren, und verschärfen können Sie später immer noch.
| Header | Wirkung | Sicherer Startwert |
|---|---|---|
| Strict-Transport-Security | Erzwingt HTTPS bei Folgebesuchen | max-age=300 |
| X-Content-Type-Options | Verhindert das Erraten von MIME-Typen | nosniff |
| Referrer-Policy | Begrenzt URL-Daten an fremde Websites | strict-origin-when-cross-origin |
| Permissions-Policy | Deaktiviert ungenutzte Browserfunktionen | camera=(), microphone=(), geolocation=() |
| Content-Security-Policy | Frame-Schutz (vorerst) | frame-ancestors 'self' |
| X-Frame-Options | Älterer Frame-Schutz | SAMEORIGIN |
HSTS ist in RFC 6797 definiert. Beginnen Sie mit fünf Minuten (max-age=300) und prüfen Sie, ob alles weiter funktioniert. Erhöhen Sie dann auf einen Tag (86400), danach auf eine Woche und schließlich auf ein Jahr (31536000). includeSubDomains ergänzen Sie erst, wenn Sie sicher wissen, dass jede Subdomain per HTTPS erreichbar ist.
Referrer-Policy mit strict-origin-when-cross-origin übermittelt an fremde Websites nur Ihre Domain, nicht die vollständige URL. Bei WordPress ist das relevant, weil URLs oft Query-Strings enthalten, etwa Suchbegriffe, Bestellnummern oder Kampagnenparameter. Die sollen nicht bei Dritten landen.
Permissions-Policy schaltet Kamera, Mikrofon und Standortzugriff für Ihre Seiten und alle eingebetteten Frames ab. Betreiben Sie eine Filialsuche, die den Standort braucht, lassen Sie geolocation aus der Liste weg.
Frame-Schutz gehört heute in die CSP, und zwar als frame-ancestors 'self'. X-Frame-Options: SAMEORIGIN behalten Sie nur noch als Rückfallebene für alte Browser. Eine kleine, erzwungene CSP, die ausschließlich frame-ancestors enthält, können Sie gefahrlos sofort ausrollen. Die vollständige CSP folgt später.
X-XSS-Protection ist veraltet. Aktivieren Sie den Header nicht. Er steuerte einen XSS-Filter, den moderne Browser entfernt haben, und dieser Filter ließ sich sogar missbrauchen, um auf ansonsten sicheren Seiten neue Probleme zu verursachen. 1; mode=block bringt Ihnen nichts. Der Ersatz ist CSP.
Wo Sie Security-Header setzen: Server, Plugin oder CDN
Legen Sie für jeden Header genau einen Ort fest. Schicken Server, Sicherheits-Plugin und CDN alle drei Strict-Transport-Security, erhalten Sie doppelte oder widersprüchliche Werte, und niemand weiß mehr, welcher davon greift.
Meist ist der Webserver der beste Ort. Serverregeln gelten für jede Antwort, also auch für Bilder, CSS-Dateien und Seiten aus einem Full-Page-Cache. Unter Apache, also bei den meisten Shared-Hosting-Tarifen, ergänzen Sie Folgendes in der .htaccess im WordPress-Hauptverzeichnis, oberhalb des Blocks # BEGIN WordPress. Voraussetzung ist mod_headers:
<IfModule mod_headers.c>
Header always set Strict-Transport-Security "max-age=300"
Header always set X-Content-Type-Options "nosniff"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"
Header always set Content-Security-Policy "frame-ancestors 'self'"
Header always set X-Frame-Options "SAMEORIGIN"
</IfModule>
Unter nginx tragen Sie die entsprechenden Zeilen in den server-Block Ihrer HTTPS-Website ein und führen vor dem Neuladen nginx -t aus:
add_header Strict-Transport-Security "max-age=300" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
add_header Content-Security-Policy "frame-ancestors 'self'" always;
add_header X-Frame-Options "SAMEORIGIN" always;
Eine Falle bei nginx sollten Sie kennen: Ein location-Block mit einer eigenen add_header-Zeile erbt keine einzige add_header-Zeile aus der server-Ebene. Ein Cache-Control-Header für /wp-content/uploads/ kann so unbemerkt sämtliche Security-Header für diese Dateien entfernen. Wiederholen Sie die Header in diesem Block oder lagern Sie sie in ein Snippet aus, das Sie überall per include einbinden.
Plugins und eigener Code über den WordPress-Hook send_headers laufen nur, wenn PHP die Seite erzeugt. Statische Dateien durchlaufen PHP nie. Auch Seiten aus einem Full-Page-Cache können PHP umgehen, je nachdem, wie der Cache Header speichert und wieder ausliefert. CDN- oder Edge-Regeln sind eine gute Alternative, weil sie alles abdecken, was über das CDN läuft. Managed Hoster bieten oft eigene Header-Einstellungen im Kundenmenü an. Wenn Ihr Hoster das tut, nutzen Sie dieses Menü und doppeln Sie die Regeln nicht in der .htaccess.
Content-Security-Policy, ohne WordPress zu beschädigen
Die CSP bringt den größten Nutzen und birgt das höchste Risiko. Sie legt fest, aus welchen Quellen der Browser Skripte, Stylesheets, Bilder, Schriften und Frames laden darf. WordPress macht es Ihnen dabei nicht leicht. Core und Block-Editor geben Inline-Skripte und -Styles aus, Page-Builder fügen weitere hinzu. Analytics-Tools, YouTube- und Karteneinbettungen, Cookie-Banner und Zahlungs-Widgets laden jeweils Code von fremden Domains. Eine strenge Richtlinie, die davon etwas blockiert, kann den Checkout unbrauchbar machen oder einen leeren Editor hinterlassen.
Beginnen Sie im Report-Only-Modus. Der Browser protokolliert Verstöße dann nur, statt etwas zu blockieren:
Content-Security-Policy-Report-Only: default-src 'self'; img-src 'self' data:; object-src 'none'; base-uri 'self'
Rufen Sie Ihre wichtigsten Seitentypen auf: Startseite, einen Blogbeitrag, eine Seite mit eingebettetem Inhalt, Warenkorb und Checkout. Öffnen Sie jeweils die Entwicklertools des Browsers und sehen Sie in die Konsole. Jeder Verstoß nennt die blockierte Ressource und die verletzte Direktive. Nutzen Sie diese Liste als Inventar aller Drittanbieter-Anfragen Ihrer Website. Häufig tauchen dabei Dinge auf, an deren Einbau sich niemand erinnert, etwa ein altes Chat-Widget oder Schriften von Google-Servern. Wie Sie mit Letzteren umgehen, erklärt unser Artikel zu Google Fonts.
Ergänzen Sie die Quellen, die Sie wirklich brauchen, entfernen Sie die überflüssigen und prüfen Sie die Konsole erneut. Erst wenn auf allen wichtigen Seiten keine Verstöße mehr auftauchen, ändern Sie den Header-Namen in Content-Security-Policy. Nehmen Sie frame-ancestors 'self' in diese vollständige Richtlinie auf: Im Report-Only-Modus ignorieren Browser frame-ancestors, und der vollständige Header ersetzt die kleine CSP, die Sie zuvor gesetzt haben. Schwierig sind die Inline-Skripte. Wer 'unsafe-inline' erlaubt, verschenkt einen Großteil des XSS-Schutzes, während Nonces oder Hashes Unterstützung durch Theme oder Plugin voraussetzen. Die CSP-Referenz bei MDN dokumentiert jede Direktive.
Überlegen Sie, wp-admin zumindest anfangs von der strengen Richtlinie auszunehmen. Unter nginx setzen Sie die Frontend-CSP dann in den location-Blöcken, die die öffentliche Website ausliefern, und nicht im gesamten server-Block.
Mit einem report-uri- oder report-to-Endpunkt können Sie Verstöße zentral sammeln. Diese Berichte enthalten Seiten-URLs und Browserdaten, und jeder kommt mit der IP-Adresse des Besuchers an. Ein externer Reporting-Dienst ist nach DSGVO in der Regel Ihr Auftragsverarbeiter, Sie brauchen also einen Auftragsverarbeitungsvertrag (Art. 28). Bei einer kleinen Website reicht es oft, die Konsole selbst auszuwerten.
HSTS-Preload: warum Sie warten sollten
Beim Preloading kommt Ihre Domain auf eine Liste, die Browser fest mitliefern. Diese Browser nutzen dann schon vor dem ersten Besuch HTTPS für Ihre Domain, und zwar für jede Subdomain. Genau darin liegt das Problem. Ein alter Webmail-Server, eine Staging-Umgebung, die Weboberfläche eines Druckers oder ein Intranet-Tool, das nur über unverschlüsseltes HTTP funktioniert, ist in diesen Browsern nicht mehr erreichbar.
Eine Domain wieder von der Liste zu nehmen ist möglich, dauert aber. Browser entfernen den Eintrag erst, wenn die Änderung per Browser-Update bei den Nutzern ankommt. Planen Sie so, als sei die Entscheidung endgültig.
Erhöhen Sie max-age schrittweise und bleiben Sie eine Weile bei den höheren Werten. Erfassen Sie jede Subdomain, die Ihr Unternehmen nutzt, auch solche, die noch im DNS stehen, aber von niemandem mehr aufgerufen werden. Denken Sie erst über Preload nach, wenn sie alle per HTTPS ausgeliefert werden. Die aktuellen Anforderungen an den Header veröffentlicht die Anmeldeseite hstspreload.org. Lesen Sie sie dort nach, statt Werte aus Blogbeiträgen zu übernehmen.
Header testen und dauerhaft im Blick behalten
Prüfen Sie die tatsächlichen Antworten, nicht Ihre Konfigurationsdateien. Öffnen Sie in den Entwicklertools des Browsers den Tab „Netzwerk“, klicken Sie auf die Dokument-Anfrage und lesen Sie die Antwort-Header. Im Terminal geht es so:
curl -sI https://example.com/
curl -sI https://example.com/wp-includes/css/dist/block-library/style.min.css
Öffentliche Header-Checker liefern einen schnellen Überblick. Das OWASP Secure Headers Project erläutert, warum die einzelnen Header empfohlen werden. Testen Sie mindestens diese fünf Antworten:
- Startseite
- Eine Seite aus dem Cache, abgerufen im abgemeldeten Zustand
- Eine statische Datei (CSS, JS oder Bild)
/wp-login.php- Eine Ansicht in wp-admin, einschließlich Block-Editor
Header verschwinden gern, ohne dass es jemand bemerkt. Typische Ursachen sind ein Hosterwechsel, bei dem Ihre .htaccess-Regeln verloren gehen, eine Umstellung am CDN oder ein neues Caching- oder Sicherheits-Plugin, das eigene Werte sendet. Prüfen Sie die Header nach jeder dieser Änderungen erneut. Dokumentieren Sie die aktive Konfiguration und das Testdatum zusammen mit Ihren übrigen Härtungsnachweisen aus der Sicherheits-Checkliste. Genau diese Dokumentation legen Sie vor, wenn ein Kundenfragebogen oder ein Prüfer danach fragt.
Das Audit-Plugin von secaudit.eu führt einen Härtungs- und Konfigurationsscan durch und ordnet die Ergebnisse Art. 32 DSGVO und Art. 21 NIS2 zu. Nutzen Sie es ergänzend zu den oben beschriebenen Header-Prüfungen → secaudit.eu
FAQ
Wirken sich Security-Header auf SEO oder Ladezeit aus?
Der Effekt ist vernachlässigbar. Sie fügen jeder Antwort einige hundert Byte hinzu. HSTS kann bei wiederholten Besuchen sogar die Weiterleitung von HTTP auf HTTPS einsparen.
Kann ein Plugin alle Security-Header für mich setzen?
Teilweise. Ein Plugin deckt die von PHP erzeugten Seiten ab, kann aber statische Dateien und gecachte Seiten verfehlen. Regeln auf Server oder CDN sind zuverlässiger.
Sollte ich X-XSS-Protection: 0 senden oder den Header weglassen?
Lassen Sie ihn weg oder senden Sie 0, falls ein Fragebogen ausdrücklich einen Wert verlangt. 0 schaltet den veralteten Filter ab. Verwenden Sie niemals 1; mode=block.
Schreibt die DSGVO HSTS vor?
Keine Vorschrift der DSGVO nennt konkrete Header. Die DSGVO verlangt aber eine angemessene Sicherheit unter Berücksichtigung des Stands der Technik. HTTPS per HSTS zu erzwingen ist eine günstige Standardmaßnahme, die zu dieser Anforderung passt.
Dieser Artikel ist eine allgemeine Information und keine Rechtsberatung.
Prüfen Sie Ihre eigene Website
Security Audit & Compliance ist das WordPress-Plugin von secaudit.eu: Es sucht nach Malware, verstecktem Spam, veränderten Kerndateien und unsicherem Code und ordnet seine Befunde DSGVO Art. 32, NIS2 Art. 21 und den OWASP Top 10 zu.
Sehen, was das Plugin prüft