Härtung der Web-Infrastruktur: Dritte Skripte per Content Security Policy (CSP) kontrollieren

Inhalt
Architektur-Überblick: Angriffsvektoren durch Drittanbieter-Skript-Injektionen
Moderne Webanwendungen und WordPress-Installationen nutzen regelmäßig JavaScript-Dateien von Drittanbietern für Analyse-Zwecke, Tag-Management, eingebettete Medien und Marketing-Automatisierung. Jede externe Skriptquelle, die in das Document Object Model (DOM) geladen wird, vergrößert jedoch die Angriffsfläche. Kompromittierte Content Delivery Networks (CDNs), Supply-Chain-Angriffe auf JavaScript-Bibliotheken oder bösartige Browser-Erweiterungen können unbemerkt nicht autorisierte Skripte in die aktive Browsersitzung einschleusen.
Ohne architektonische Kontrollen auf HTTP-Header-Ebene führen Browser jedes im DOM vorhandene Skript ausnahmslos aus. Dies birgt erhebliche Risiken für Cross-Site Scripting (XSS), DOM-Scraping und unbefugte Datenexfiltration (z. B. Form-Skimming oder Cookie-Diebstahl). Die Implementierung einer restriktiven Content Security Policy (CSP) etabliert ein unveränderliches Berechtigungssystem direkt im Client-Browser, das garantiert, dass ausschließlich explizit autorisierte Domains und kryptografische Hashes Code ausführen oder Daten übertragen dürfen.

googletagmanager.com steht auf der Erlaubnisliste, eingeschleuste Skripte und object-Einbettungen nicht.Schritt-für-Schritt-Implementierungsanleitung
Schritt 1: Vollständiges Skript- und Endpunkt-Audit durchführen
Vor der Aktivierung eines blockierenden CSP-Headers müssen sämtliche legitimen Domain-Abhängigkeiten inventarisiert werden. In einer WordPress-v7.0.2-Umgebung mit professionellem Tag-Management und serverseitigem Tracking identifiziert ein Audit typischerweise folgende Basis-Direktiven:
default-src 'self'– Beschränkt das Laden von Standard-Ressourcen strikt auf die eigene Domain.script-src 'self'– Erlaubt ausschließlich Skripte, die vom primären Server stammen.connect-src 'self'– Begrenzt REST-API-, Fetch- und XHR-Verbindungen auf vertrauenswürdige Endpunkte.img-src 'self' data:– Gestattet lokale Grafiken und eingebettete Base64-Daten-URIs.
Schritt 2: Konzeption einer CSP-Richtlinie mit minimalen Privilegien
Um das Einschleusen unerwünschter Tracking-Skripte zu blockieren, gleichzeitig aber den Betrieb autorisierter Systeme (wie Google Tag Manager, GA4, Matomo oder einen eigenen Server-Side-GTM-Endpunkt) zu gewährleisten, müssen strikte Whitelists definiert werden. Eine sichere Baseline-Konfiguration für Enterprise-Analytics-Anforderungen lautet:
default-src 'self';
script-src 'self' 'unsafe-inline' https://www.googletagmanager.com https://cdn.matomo.cloud;
img-src 'self' data: https://www.google-analytics.com https://www.googletagmanager.com;
connect-src 'self' https://www.google-analytics.com https://region1.google-analytics.com https://matomo.cloud https://tracking.lukaswojcik.com;
frame-src 'self' https://www.youtube.com;
object-src 'none';
base-uri 'self';
form-action 'self';
Hinweis: Die Verwendung von 'unsafe-inline' innerhalb von script-src sollte in Hochsicherheitsumgebungen durch dynamische kryptografische Nonces oder SHA-256-Skripthashes ersetzt werden.
Schritt 3: Server-Deployment über Nginx-Konfiguration
Aus Performance-Gründen sollten CSP-Header direkt vom Webserver gesendet werden, anstatt sie auf Anwendungsebene von PHP erzeugen zu lassen. Die Integration im SSL-Server-Block von Nginx garantiert die Ausführung noch vor der PHP-Verarbeitung:
# /etc/nginx/sites-available/lukaswojcik.com.conf
server {
listen 443 ssl http2;
server_name www.lukaswojcik.com lukaswojcik.com;
# Strikte CSP-Header-Durchsetzung
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://www.googletagmanager.com https://cdn.matomo.cloud; img-src 'self' data: https://www.google-analytics.com https://www.googletagmanager.com; connect-src 'self' https://www.google-analytics.com https://region1.google-analytics.com https://matomo.cloud https://tracking.lukaswojcik.com; frame-src 'self' https://www.youtube.com; object-src 'none'; base-uri 'self'; form-action 'self';" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
}
Nach der Anpassung der Nginx-Konfiguration sind eine Syntaxprüfung und ein Service-Reload zwingend erforderlich:
nginx -t && systemctl reload nginx
Schritt 4: Applikations-Fallback über WordPress functions.php
Falls kein Root-Zugriff auf die Nginx-Konfiguration besteht, lässt sich der identische CSP-Header über PHP während des WordPress-Request-Zyklus ausliefern, indem folgende Logik in die Datei functions.php des aktiven Themes eingebunden wird:
/**
* Content Security Policy Header Enforcement
* Zielsystem: WordPress v7.0.2
*/
add_action( 'send_headers', 'lw_enforce_content_security_policy' );
function lw_enforce_content_security_policy() {
if ( ! is_admin() ) {
$csp = "default-src 'self'; " .
"script-src 'self' 'unsafe-inline' https://www.googletagmanager.com https://cdn.matomo.cloud; " .
"img-src 'self' data: https://www.google-analytics.com https://www.googletagmanager.com; " .
"connect-src 'self' https://www.google-analytics.com https://region1.google-analytics.com https://matomo.cloud https://tracking.lukaswojcik.com; " .
"frame-src 'self' https://www.youtube.com; " .
"object-src 'none'; " .
"base-uri 'self'; " .
"form-action 'self';";
header( 'Content-Security-Policy: ' . $csp );
}
}
Schritt 5: Qualitätssicherung und Report-Only-Testlauf
Vor der Aktivierung strikter Blockaden im Live-Betrieb empfiehlt sich ein Deployment als Content-Security-Policy-Report-Only. Dadurch werden Richtlinienverstöße in der Browser-Konsole protokolliert, ohne dass Skripte gestoppt werden.
Die Verifikation muss mittels der Browser-Entwicklertools (F12) erfolgen:
- Prüfung der Netzwerk-Header: Den Netzwerk-Tab öffnen, die Seite neu laden und bestätigen, dass der HTTP-Response-Header den vollständigen
Content-Security-Policy-String enthält. - Konsolen-Analyse auf Anomalien: Den Konsolen-Tab überwachen. Nicht freigegebene Tracking-Tags erzeugen sofort deutliche rote CSP-Fehlermeldungen. Inhaltsskripte von Browser-Erweiterungen unterliegen der CSP der Seite dagegen nicht; gegen die Richtlinie geprüft wird nur, was sie als Skript-Element in die Seite einfügen.
- Validierung der Tracking-Endpunkte: Sicherstellen, dass serverseitige Tracking-Requests an eigene Analytics-Subdomains mit dem HTTP-Statuscode 200 ohne Verbindungsabbruch verarbeitet werden.
Zusammenfassung und resultierender Mehrwert
Was sich damit erreichen lässt: Die unkontrollierte Ausführung beliebiger JavaScript-Dateien im DOM wird durch eine kryptografisch abgesicherte, domänenspezifische Whitelist auf Ebene der Browser-Security-Engine abgelöst.
Resultierender Mehrwert:
- Schutz vor Supply-Chain-Angriffen: Kompromittierte externe JavaScript-Bibliotheken oder fremde Drittanbieter-Skripte werden sofort an der Ausführung und Datenexfiltration gehindert.
- DSGVO- und Datenschutz-Konformität: Unautorisierte Tracking-Pixel und Piggyback-Tags werden systematisch blockiert, bevor eine Verbindung zu externen Werbenetzwerken entsteht.
- Härtung von Formularen und Sitzungen: Die Restriktion von
connect-srcundform-actionverhindert Form-Skimming und Credential-Harvesting auf sämtlichen WordPress-Seiten.
Fragen und Antworten
Warum fehlt die CSP aus der Nginx-Konfiguration auf manchen Adressen?
Wegen der Vererbungsregel von add_header. Nginx übernimmt add_header-Anweisungen aus dem server-Block nur in location-Blöcke, die selbst keine einzige add_header-Anweisung enthalten. Setzt ein location-Block etwa für Bilder, Schriften oder PHP einen eigenen Header wie Cache-Control, entfallen dort alle Header aus dem server-Block, auch Content-Security-Policy, X-Content-Type-Options und X-Frame-Options.
Das bleibt oft lange unbemerkt, weil die Prüfung aus Schritt 5 meist an einer HTML-Seite erfolgt. Sicherer ist eine Stichprobe je location-Block, also auch für Bilder, Skripte und die Adressen, die PHP ausliefert.
Abhilfe schafft, die Header in jedem location-Block zu wiederholen, der eigene add_header-Zeilen hat, am einfachsten über eine gemeinsame Datei, die per include eingebunden wird.
Was passiert, wenn Nginx und die functions.php beide eine CSP senden?
Dann gelten beide. Der Browser setzt mehrere Content-Security-Policy-Header nicht zu einer Richtlinie zusammen, sondern prüft jede Ressource gegen jede von ihnen; geladen wird nur, was alle erlauben. Eine Domain, die nur in einer der beiden Fassungen steht, bleibt also blockiert. Das passiert leicht, wenn eine Quelle nur an einer Stelle nachgetragen wird, etwa in der Nginx-Konfiguration, aber nicht in der functions.php.
Umgekehrt kann der Header aus der functions.php fehlen, obwohl der Code stimmt. Ein Seitencache, der fertige HTML-Dateien direkt vom Webserver ausliefert, ohne PHP zu starten, liefert sie ohne diesen Header aus, sofern er die Kopfzeilen nicht mitspeichert. Wer die Richtlinie über PHP setzt, sollte deshalb auch eine Seite aus dem Cache prüfen, nicht nur eine frisch erzeugte.
Wie lassen sich Verstöße erfassen, die nur bei echten Besuchern auftreten?
Über eine Meldeadresse in der Richtlinie. Die Konsole zeigt Verstöße nur im Browser dessen, der gerade prüft; mit der Anweisung report-uri oder dem neueren report-to samt Header Reporting-Endpoints schicken die Browser der Besucher jeden Verstoß als JSON an einen eigenen Endpunkt, auch im Report-Only-Betrieb. Solche Meldungen enthalten auch Rauschen, etwa von Browser-Erweiterungen, und sollten deshalb nach Quelle ausgewertet werden statt nach bloßer Anzahl.