TL;DR: Die Website einer deutschen Kanzlei machte Consent besser als alles, was ich je gemessen habe: Ein frisches Profil lud null Cookies, null Drittanbieter-Requests, null Iframes — nur der Consent-Manager selbst, Fonts lokal ausgeliefert. Sieben Tage nach Veröffentlichung des Fixes für eine unauthentifizierte Theme-XSS lief die Seite noch mit der verwundbaren Version, ohne CSP, die den Payload hätte eindämmen können. Dazu null von sechs Security-Headern auf der gesamten Oberfläche, ein anonymer CSRF-Token-Endpunkt auf der REST-API, öffentlich lesbare readme/license-Dateien und eine DMARC-Policy, die gefälschte Mandanten-Mails durchwinkt. Zwei Teams, zwei Reifegrade, eine Origin — und der schwächere bestimmt das Risiko.
Die Ausgangslage
Der Klient ist eine deutsche Kanzlei. Mandantengelder bewegen sich dort auf schriftliche Anweisung — E-Mail-Authentizität ist also kein Compliance-Thema, sondern eine operative Kontrolle. Meine Aufgabe war eine Prüfung der öffentlichen Web-Oberfläche: nicht-intrusiv, keine Payloads gegen den Live-Host, keine Anmeldeversuche. Header, DNS, TLS, REST-Proben und ein Headless-Browser mit frischem Profil.
Der erste Test ist immer Consent, weil er zeigt, wie eine Seite mit Besuchern umgeht, denen sie nichts schuldet. Er kam grün zurück, wie ich es noch nicht gesehen hatte: null Cookies, null Drittanbieter-Requests, null Iframes vor jeder Interaktion. Nur der Consent-Manager selbst lud. Google Fonts lokal. Das ist Lehrbuch — besser als Lehrbuch, weil die meisten Seiten mit Consent-Tool trotzdem irgendetwas feuern.
Dann lief dasselbe frische Profil gegen den Rest der Oberfläche — und das Bild teilte sich in zwei Hälften.
Ich habe alle identifizierenden Informationen entfernt. Das Ziel wird nur nach Branche und Technologieklasse beschrieben, damit das Muster wiederverwendbar bleibt.
Das Muster
Reife in einer Dimension überträgt sich nicht
Wer den Consent-Stack konfiguriert hat, kümmerte sich, testete und verstand das Thema. Kein Drittanbieter-Request vor Einwilligung ist kein Standard — das ist eine Entscheidung, die jemand getroffen und überprüft hat. Dieselbe Seite lieferte null von sechs Basis-Security-Headern auf jeder Seite. Beide Fakten existierten aus demselben Grund nebeneinander, aus dem sie überall nebeneinander existieren: Datenschutz und Sicherheit haben verschiedene Verantwortliche, verschiedene Ticket-Warteschlangen, verschiedene Definitionen von „fertig". Eine Prüfung, die beim ersten grünen Bereich stehen bleibt, verpasst den Befund. Man muss jede Dimension einzeln messen, denn Reife sickert organisatorisch nicht seitwärts durch.
Ein Patch-Fenster ist ein öffentlicher Countdown, keine interne Notiz
Das Theme der Seite war einen Patch hinter einem unauthentifizierten reflected XSS, dessen Fix seit sieben Tagen öffentlich war. Die Startseite verrät die Version. Diese Version gegen aktuelle Advisories zu mappen dauert Minuten. Die Lücke zwischen „Fix veröffentlicht" und „installiert" ist von außen messbar, und jeder Scanner berechnet sie automatisch. Das Unangenehme ist nicht die Schwachstelle — es ist die Tatsache, dass ein Sieben-Tage-Fenster von außen nicht von einer nie gepatchten Seite zu unterscheiden ist. Die einzige Verteidigung ist, das Fenster zu schließen, bevor es zur Statistik wird.
„Der Login ist gehärtet" beweist Fähigkeit, nicht Abdeckung
Die Login-Seite hatte X-Frame-Options, eine frame-ancestors-Policy und ein sicheres Test-Cookie. Jede andere Seite hatte nichts. Diese Asymmetrie ist diagnostisch: Jemand in der Organisation kannte die Header-Basislinie und hat sie genau einmal angewandt — auf der Seite, die historisch angegriffen wird. Aber eine reflected XSS kümmert sich nicht um das Login-Formular — sie führt im Origin der Seite aus, auf der das Opfer gerade ist, und ein eingeloggtes Opfer ist genau das Ziel. Abdeckung ist die Metrik, nicht Fähigkeit.
Ein anonymer Nonce-Endpunkt ist ein vorgebautes Angriffswerkzeug
Der REST-Index umfasste 328 KB Routennamen — inklusive Security-Plugin- und Editor-Namespaces — und ein Endpunkt gab jedem anonymen Aufrufer einen CSRF-Nonce plus den Admin-Pfad zurück. Die Benutzer-Enumeration war an derselben API korrekt gesperrt, was zeigt: Die Sperre war bewusst, aber unvollständig. Token-Endpunkte sind das Schlimmste, was offen bleiben kann: Enumeration gibt dem Angreifer eine Karte, ein Nonce gibt ihm einen Schlüsselrohling, den er später zuschneiden kann.
In manchen Branchen ist E-Mail das Perimeter
SPF endete auf -all — den Teil bekommen alle hin. DMARC stand auf p=quarantine ohne gefundenen DKIM-Selector — fehlgeschlagene Alignment-Mails landen also in der Quarantäne statt im Ablehnungsordner, und die Spam-Entscheidung eines Mailbox-Providers wird zur letzten Verteidigungslinie. Für eine Kanzlei ist der realistische Betrug kein Spam: Es ist eine gefälschte Anweisung an einen Mandanten, mit neuer Bankverbindung. Diese Bedrohung berührt die Website nie. Sie entscheidet sich vollständig über DMARC und DKIM — was sie zu Mandantenschutz-Kontrollen macht, nicht zu Hygiene.
Die Kontrolle, die funktionierte — und den Auftrag prägte
Das Security-Plugin hat uns nach rund sechzig Requests von der Seite verbannt. Das ist eine funktionierende Kontrolle, und das gehört gesagt: Die meisten Seiten, die ich teste, reagieren überhaupt nicht. Sie hat auch den Audit diszipliniert — der Block wurde respektiert, betroffene Proben nach seiner Aufhebung nachgezogen, die Zeitleiste ist in den Belegen dokumentiert. Eine Verteidigung, die den Zeitplan eines Angreifers verändert, ist eine Verteidigung, die ihren Job macht.
Befunde
| Schwere | Befund | Beleg |
|---|---|---|
| HOCH | Unauthentifizierte reflected XSS im Theme, sieben Tage nach dem öffentlichen Fix — keine CSP, die sie eindämmt | Version auf der Leitung vs. Advisory-Zeitleiste; Lab-Spiegel gibt den Payload unescaped aus |
| HOCH | Null von sechs Basis-Security-Headern auf jeder Seite außer dem Login | Header-Capture von Startseite, Kontakt und Login im Direktvergleich |
| MITTEL | REST-Index (328 KB) anonym; ein Endpunkt liefert CSRF-Nonce und Admin-Pfad ohne Login | REST-Capture; Antwort des Token-Endpunkts |
| MITTEL | wp-cron.php öffentlich erreichbar — unauthentifizierter Cron-Trigger | HTTP 200 auf der Live-Seite |
| MITTEL | DMARC nur p=quarantine, kein DKIM-Selector — gefälschte Mandanten-Mails werden quarantäniert, nicht abgelehnt | DNS-TXT-Records |
| MITTEL | Kein SRI auf den externen Skripten des Consent-Anbieters | Startseiten-HTML |
| NIEDRIG | readme.html und license.txt offen; Seiten zählbar (25 Seiten, 28 Beiträge, 181 Medien); ftp/dev-Alt-Hosts auf Fremdservern | Pfad-Proben, REST-Zähler, DNS |
| NIEDRIG | CMP-Storage-Eintrag vor Consent; 90-Tage-Zertifikatszyklus ohne HSTS | Headless-Capture, TLS-Datensatz |
| GUT | Consent-Architektur: null Cookies, null Drittanbieter-Requests, null Iframes vor Opt-in; harte Pfade 403; Benutzer-Enumeration dreifach gesperrt; Rate-Limiting funktionierte | Frisch-Profil-Capture; Pfad-Proben; der Block selbst |
Der Angriffspfad
Reproduktion im Labor
Jede Probe unten zielt auf einen Lab-Spiegel, den ich kontrolliere. Nichts davon richtet sich gegen ein Live-System.
# 1. die Kontrolle: Consent in einem frischen Profil
TARGET_URL=http://lab.local/ node consent-audit.js
# <- Cookies: 0 Drittanbieter-Hosts: nur die CMP Banner: vorhanden
# 2. dieselbe Origin, die sechs Header
curl -sI http://lab.local/ | grep -ciE \
'strict-transport|content-security|x-frame|x-content-type|referrer-policy|permissions-policy'
# <- 0
curl -sI http://lab.local/wp-login.php | grep -iE 'x-frame|content-security'
# <- der Login ist gehärtet — Fähigkeit, nicht Abdeckung
# 3. Version vs. Advisory
curl -s http://lab.local/ | grep -oE 'ver=[0-9.]+' | sort -u
# <- Theme-Version ein Release hinter dem Fix
# 4. der Payload, nur im Labor
curl -s 'http://lab.local/?lang=%22%3E%3Cimg%20src%3Dx%20onerror%3Dalert(1)%3E' \
| grep -o 'img src=x'
# <- verwundbarer Build gibt ihn roh aus; gehärteter Build liefert <img
# 5. der Token-Endpunkt
curl -s http://lab.local/wp-json/security-plugin/v1/authenticate
# <- verwundbar: {success, nonce, adminUrl}; gehärtet: 401
# Zusicherungen des Lab-Spiegels, verwundbar vs. gehärtet:
# Security-Header-Anzahl 7 Checks: 0 -> 6
# lang-Reflexion roh -> escaped
# readme.html / license.txt 200 -> 404
# REST-Nutzerliste 200 mit Klarnamen -> 401
# Token-Endpunkt 200 + Nonce -> 401
# wp-cron.php 200 -> 403
Erkennung im eigenen Bestand
- Führe Consent-Test und Sicherheits-Test als getrennte Drills mit getrennten Verantwortlichen durch. Ein grünes Datenschutz-Ergebnis kann ein ungepatchtes Theme monatelang verstecken, weil niemand das Sicherheits-Backlog nach einem Compliance-Erfolg wieder aufmacht.
- Verfolge Patch-Fenster als Zahl: pro Komponente die Tage zwischen Fix-Veröffentlichung und Deployment. Alarmiere oberhalb deiner Policy-Schwelle, statt auf den nächsten Release-Zug zu warten.
- Prüfe die Header-Basislinie auf jedem Vhost und jeder Routenklasse, nicht nur am Login. Wenn der Login die einzige gehärtete Seite ist, wurde die Härtung per Hand gemacht — automatisier sie, oder sie wird sich nie ausbreiten.
- Inventarisiere REST-Endpunkte danach, was sie anonymen Aufrufern zurückgeben. Jeder Endpunkt, der ein Nonce, Token oder URL liefert, ist ein Befund — auch wenn die Benutzer-Enumeration anderswo gesperrt ist.
- Behandle DMARC und DKIM als Mandantenschutz-Kontrollen: erst DKIM, dann
p=reject, sobald die Reports sauber bleiben. Quarantäne ist ein Vorschlag; Reject ist eine Entscheidung. - Messe die eigenen Verteidigungen von außen. Das Rate-Limit dieser Seite feuerte und veränderte den Auftrag — kenne die Schwelle und verifiziere, dass sie funktioniert, bevor ein Angreifer es tut.
Behebung
# sofort
1. Theme auf den gefixten Release aktualisieren; neue Version auf der Leitung verifizieren
2. Header-Basislinie überall ausliefern (HSTS, CSP, X-Frame-Options, nosniff,
Referrer-Policy, Permissions-Policy) — per .htaccess oder Vhost, nicht pro Seite
# innerhalb einer Woche
3. REST-API auf eingeloggte Nutzer beschränken; Vendor- und Token-Endpunkte
für anonyme Aufrufer deaktivieren
4. readme.html, license.txt und wp-cron.php sperren; auf DISABLE_WP_CRON
mit System-Cron umstellen
5. DKIM am Mail-Gateway aktivieren und Selector publizieren; Umstieg auf
DMARC p=reject nach 30 sauberen Report-Tagen planen
# innerhalb eines Monats
6. SRI oder CSP-Hashes für den Consent-Anbieter; security.txt (200);
CAA ergänzen, Alt-Hosts inventarisieren, Zertifikats-Renewal-Alerting
Once an attacker gains access, they can perform the following actions:
Erkenntnisse
- Datenschutz-Reife überträgt sich nicht auf Sicherheits-Reife. Sie haben verschiedene Verantwortliche, verschiedene Warteschlangen — und die schwächere bestimmt das Risiko.
- Ein Patch-Fenster ist extern, öffentlich und zählbar: Version auf der Leitung plus Fix-Veröffentlichungsdatum. Sieben Tage ungepatcht und nie gepatcht sehen von außen identisch aus.
- Eine gehärtete Seite beweist, dass jemand sich gekümmert hat. Jede Route gehärtet beweist, dass die Kontrolle existiert. Reflected XSS zielt auf die Seiten, die du nicht gehärtet hast.
- Token-Endpunkte sind schlimmer als Enumeration. Eine Karte ist Aufklärung; ein Nonce ist ein Angriffswerkzeug.
- In professionellen Dienstleistungen ist E-Mail-Authentizität Mandantenschutz. SPF mit
-allist der Anfang; DKIM undp=rejectsind das Ende. - Lob, was funktioniert. Die Consent-Schicht und der Rate-Limiter funktionierten wirklich — der Bericht sagt es, weil Sicherheitsberatung, die nur Fehler zählt, keine Beratung ist, sondern eine Anzeigetafel.