Datenschutz, IT-Security, Linux, Server, Wordpress

Sicherheit und Datenschutz mit Content-Security-Policy Header für WordPress

Heute wollen wir uns mit Content Security Policy (CSP) Headern beschäftigen. In erster Linie ist dies ein Sicherheitsfeature, um das Laden von (Java-)Skripten, aber auch Bilder, Fonts, iFrames etc. aus böswilligen Quellen zu unterbinden und so insbesondere Cross-Site-Scripting zu unterbinden. Wikipedia sagt dazu folgendes:

Content Security Policy (CSP) ist ein Sicherheitskonzept, um Cross-Site-Scripting und andere Angriffe durch Einschleusen von Daten in Webseiten zu verhindern.

In der Praxis sind jedoch Cross-Site-Scripting-Schwachstellen sehr verbreitet. Die Content Security Policy erzwingt eine strikte Trennung zwischen Inhaltsdaten im HTML-Code und externen Dateien z.B. mit JavaScript-Code. In der Regel ist der JavaScript-Code statisch und wird nicht dynamisch generiert.

Content Security Policy, Wikipedia

Wir wollen CSP aber für noch mehr benutzen: Wir können damit auch unerwünschte Drittanbieter ausschließen, die uns WordPress quasi aufzwingt. Diese Drittanbieter können die Besucher eurer Webseite möglicherweise (in einigen Fällen sogar ziemlich sicher) tracken. Wer seinen Benutzern gegenüber Datenschutzfreundlich sein möchte, verzichtet nicht nur auf Google Analytics, sondern auch auf andere Drittanbieter. Dennoch möchte ich das Analysieren von Benutzerverhalten nicht gänzlich verteufeln, denn es liefert nützliche Einsichten, welche Reichweite man mit seinem Angebot erzielt und welche Angebote die Besucher womöglich besonders interessieren. Aber dazu gibt es auch Datenschutzfreundliche Alternativen: Matomo ist ein Paradebeispiel, welche ein selbst gehostetes System darstellt und freingranulare Einstellungen im Bezug auf Datenschutz bietet. Aus meiner Sicht ist der wichtigste Punkt, dass die Daten in den eigenen Händen bleiben und nicht z.B. bei Google landen. Ich beschreibe daher hier auch wie Matomo mit CSP funktioniert, wenn dieses auf einer Subdomain wie https://matomo.the-digital-native.de betrieben wird.

Für WordPress ganz übliche Drittanbeiter sind:

  • Gravatar: Verknüpft beim Dienstanbieter hinterlegte Avatare mit der E-Mailadresse. Sammelt möglicherweise Daten und wird ohnehin von nur wenigen Nutzern gebraucht. Damit Benutzer trotzdem Avatare verwenden können, ist es ratsam das Plugin WP User Avatar als Ersatz zu installieren.
  • Google Fonts: Stellt zentral und kostenlos Schriftarten zur Verfügung, die durch Webdesigner einfach als Fremdresource eingebunden werden können. Eine der ärgerlichsten Datenschutzsünden, denn Google erfährt so von jedem Besucher! Außerdem ist es wirklich sehr einfach möglich, Fonts auch auf dem eigenen Server abzulegen und per CSS nachzuladen. Es gibt also nicht mal einen guten Grund auf diesen Service zurück zu greifen.

Basiskonfiguration für WordPress

Folgende Konfiguration habe ich ausführlich getestet und kann im VirtualHost von apache2 so in der Form hinterlegt werden:

Header always set Content-Security-Policy \
  "default-src 'none'; \
  img-src 'self' data: \
  style-src 'self' 'unsafe-inline'; \
  font-src 'self' data:; \
  script-src 'self' 'unsafe-inline' 'unsafe-eval'; \
  frame-src 'self' \
  form-action 'self'; \
  base-uri 'none'; \
  frame-ancestors 'self'"

Was bedeuten diese Optionen nun?

  • default-src wird immer dann angewandt, wenn eine Ressource geladen werden soll, auf die keine andere Regel zutrifft. Es wird hier empfohlen none zu setzen. Wer ein Kontakt-Formular wie Contact Form 7 einsetzt, wird hier leider zumindest self setzen müssen. Theoretisch sollte das auch anders gehen (via Direktive connect-src auf self). Leider funktioniert dies in der Praxis nicht.
  • img-src definiert, woher Bilder per <img>-Tag geladen werden können. self benötigt man hier logischerweise. Die Option data: ermöglicht das Laden von Bildern aus im HTML codierten Bildmaterial. Dies nutzt WordPress ebenfalls. Wer weitere Quellen für Bilder hat, muss diese hier angeben. Hier schließen wir u.a. Gravatar aus. Wer seinen Benutzern trotz allem Gravatar zur Verfügung stellen möchte, muss hier die URL https://secure.gravatar.com hinzufügen.
  • style-css gibt an, woher CSS-Dateien geladen werden können. Leider muss hier die unsichere Option unsafe-inline hinzugefügt werden, weil WordPress mittels <style>-Tag CSS-Attribute in das HTML einbettet. Dies ist ein Sicherheitsdefizit von WordPress. Wir schließen hier insbesondere auch Google Fonts aus, denn darauf kann man bei auch nur etwas Liebe zum Datenschutz wirklich verzichten.
  • font-src gibt Möglichkeiten zum Nachladen von Schriftarten an. Werden nur Systemschriftarten eingesetzt, kann hier none gewählt werden. WordPress lädt aber, je nach Theme, Schriftarten nach. Zum Teil auch eingebettete Schriftarten, sodass data: nötig wird.
  • script-src gibt insbesondere die Quellen für JavaScripte an. Dass wir hier self haben ist ok, jedoch erfordert WordPress auch unsafe-inline und unsafe-eval, was ein besonderes Sicherheitsrisiko darstellt. Letzteres ermöglicht das Ausführen von JavaScript-Code mit der so genannten eval()-Funktion und gibt als typischer Punkt, um Schadcode einzuschleusen. Die Entwickler von WordPress sollten hier dringend nachbessern und auch ohne unsafe-eval auskommen.
  • frame-src ermöglicht das Laden von Webseiten in <frame> oder <iframe> Elementen. Sollte aus Sicherheitsgründen auf self beschränkt bleiben, falls möglich.
  • form-action beschränkt die Webseiten, die durch das Absenden eines Formulars erreicht werden können. Für Funktionen wie Kontaktformulare, Newsletteranmeldung (lokal) etc. sollte hier self genügen.
  • Setzt man base-uri auf none, muss jede verlinkte Ressource mit einem vollständigen Pfad angegeben werden. Damit wäre z.B. <img src="my_picture.png"> nicht zulässig, sehr wohl aber <img src="https://the-digital-native.de/my_picture.png">. Erfreulicherweise setzt Wodpress dieses Sicherheitsfeature um!
  • frame-ancestors erlaubt bzw. verbietet, dass die eigene Webseite in andere Webseiten eingebunden werden kann, z.B. per iframe. Die Option self verhindert möglicherweise Missbrauch des eigenen Contents durch andere Webseitenbetreiber.

Konfiguration von Matomo

Ich empfehle Matomo auf der eigenen Domain zu hosten, z.B. als Subdomain. In dem Fall muss Matomo in img-src sowie script-src hinzugefügt werden. Wenn man die Opt-Out-Box von Matomo z.B. in die Datenschutzerklärung einbinden möchte, muss Matomo auch als frame-src zulassen, da es mittels iFrame eingebunden wird.

Header always set Content-Security-Policy \
  "default-src 'none'; \
  img-src 'self' data:  https://matomo.the-digital-native.de; \
  style-src 'self' 'unsafe-inline'; \
  font-src 'self' data:; \
  script-src 'self' https://matomo.the-digital-native.de 'unsafe-inline' 'unsafe-eval'; \
  frame-src 'self' https://matomo.the-digital-native.de; \
  form-action 'self'; \
  base-uri 'none'; \
  frame-ancestors 'self'"

Konfiguration für WordPress Administration

Die WordPress-Administration benötigt etwas mehr Rechte, um korrekt zu funktionieren. Da ich die Rechte für die Benutzerspezifischen Seiten nicht unnötig ausweiten möchte, habe ich in apache2 eine Location-Direktive erstellt, die auf das Unterverzeichnis wp-admin abzielt.

<Location ~ "/wp-admin">
Header always set Content-Security-Policy \
  "default-src 'self'; \
  img-src 'self' data: https://ps.w.org https://s.w.org; \
  style-src 'self' 'unsafe-inline'; \
  font-src 'self' data:; \
  script-src 'self' 'unsafe-inline' 'unsafe-eval'; \
  form-action 'self'; \
  base-uri 'none'; \
  frame-ancestors 'self'"
</Location>

Konfiguration von Matomo

Auch hier braucht eine Matomo Instanz ein paar extra-Regeln, um die Einbettung in das Adminmenü zu ermöglichen. Eine Konfiguration kann wie folgt aussehen:

<Location ~ "/wp-admin">
Header always set Content-Security-Policy \
  "default-src 'self'  https://matomo.the-digital-native.de; \
  img-src 'self' data: https://ps.w.org https://s.w.org  https://matomo.the-digital-native.de; \
  style-src 'self' 'unsafe-inline'; \
  font-src 'self' data:; \
  script-src 'self' https://matomo.the-digital-native.de 'unsafe-inline' 'unsafe-eval'; \
  form-action 'self'; \
  base-uri 'none'; \
  frame-ancestors 'self'"
</Location>

Weitere Plugins

Wer das Plugin Yoast zur SEO-Optimierung benutzt, muss unter default-src den Eintrag https://yoast.com hinzufügen.

Generell gilt, dass ihr bei weiteren Plugins immer überprüfen solltet, ob diese auch funktionieren. Unter F12 gibt es in Firefox & Chrome die Console, wo durch CSP geblockte Inhalte angezeigt werden. Surft damit durch eure Webseite und auch das Admin-Menü. Fügt, falls nötig, weitere Webseiten zu den einzelnen Policies hinzu. Bedenkt aber: Lokale Lösungen sind Drittanbietern aus Datenschutzsicht immer vorzuziehen! Verwendet z.B. einen eigenen Captcha-Generator statt Googles ReCaptcha. Ja, es gibt viele Dienste, die euren Benutzern tolle Funktionalitäten bieten, aber wägt immer zwischen Datenschutz und Nice-To-Have Features ab!

Qualität der CSPs testen

Dafür gibt es das Mozilla Obversatory. Trotz dass wir das Maximum rausgeholt haben und einige grüne Häkchen sammeln, bekommen wir aktuell leider -20 Punkte. Kaputt macht uns das Ranking unsafe-inline und unsafe-eval. Hier sind die Softwareentwickler von WordPress gefragt, die hier als einziges Abhilfe schaffen können. Es bleibt Verbesserungspotential!

Mozilla Obversatory für https://the-digital-native.de

Garvatar Ersetzen

Fazit

Mit Content Security Policy Headern kann man eine Menge an zusätzlicher Sicherheit und Datenschutz für seine Nutzer erzielen. Eine gut getestete Grundlage für eine WordPress-Basisinstallation sowie eine Matomo Instanz haben wir hier geliefert. Bereits dies war tagelange Ausprobiererei, bis wir das Optimum an CSPs erreicht haben, ohne das die Funktionalität Schaden genommen hat. Komplexere Installationen benötigen hier evtl. nochmals mehr Aufwand.

Schreibe eine Antwort