TL;DR: Ein kostenpflichtiger API-Schlüssel stand in der Seite, und er funktionierte. Ich habe den Geocoding-Key aus einem Bewertungs-Widget extrahiert, verifiziert, dass er von einem Angreifer-Origin live ist, und per Referrer-Restriktion plus Server-Proxy behoben.
Ausgangslage
Ich habe das Ziel als Black-Box geprüft: keine Zugangsdaten, nur die öffentlich sichtbare Fläche. Das ist, was ich sah, bevor ich etwas angefasst habe.
Eine Kanzleiseite auf einer unterstützten Content-Plattform, durch Basic-Authentifizierung geschützt, mit einem Bewertungs-Widget auf den Kanzleiseiten. Geprüft wurden exponierte Zugangsdaten im Client-HTML, Header-Policy, E-Mail-Authentifizierung, Benutzer-Enumeration und die Login-Perimeter.
Ich habe alle identifizierenden Informationen entfernt. Das Ziel wird ausschließlich nach Branche und Technologieklasse beschrieben, damit das Muster übertragbar bleibt.
Das Muster
Das Muster, das ich erkannt habe — und das ich in ähnlichen Landschaften immer wieder finde:
Ein Schlüssel im Client-Code ist nicht automatisch eine Schwachstelle, weil der Browser eine Kennung braucht, um einen Drittanbieter-Dienst aufzurufen. Der Unterschied liegt zwischen einem Key, der nur die Anfrage einordnet, und einem Key, der berechnet werden kann. Ein Geocoding-Key, der Adressen auflöst, ist abrechenbar.
Bewertungs-Widgets sind die übliche Quelle. Ein Plugin bettet einen konfigurierten Key in die Seite ein, um eine Karte oder eine Bewertungskomponente zu rendern. Ob das zählt, hängt vollständig davon ab, wie der Key beim Anbieter erstellt wurde.
Die zweite Lehre betrifft Schichtung. Die Seite lag hinter Basic Authentifizierung, wirklich selten und wirklich wirksam gegen Scans. Sie hatte zugleich Benutzer-Enumeration über zwei Pfade, fehlende Header und keine Mail-Durchsetzung. Gute und schlechte Entscheidungen existierten nebeneinander, weil sie von verschiedenen Menschen zu verschiedenen Zeiten getroffen wurden.
Befunde
| Schweregrad | Befund | Nachweis |
|---|---|---|
| HOCH | Kostenpflichtiger Drittanbieter-API-Schlüssel im öffentlichen HTML gerendert | Von jedem Besucher lesbar und außerhalb der Site nutzbar |
| MITTEL | Keine Sicherheits-Header auf der öffentlichen Oberfläche | HSTS, Frameschutz und Content-Type-Schutz fehlen |
| MITTEL | Keine Enforcement-Policy für die Absenderdomain | Authentifizierung für die Mail-Domain fehlt |
| MITTEL | Benutzer-Enumeration über zwei unabhängige Pfade | Autorenkennung und Slug ableitbar |
| NIEDRIG | Sehr lange ungepflegtes Plugin weiterhin installiert | Jahre ohne Release, weiterhin aktiv |
| INFO | Login und Admin hinter Basic-Authentifizierung | Wirksam gegen automatisierte Scans |
Der Angriffspfad
Reproduktion im Labor
Jeder Befehl unten zielt auf einen Lab-Container, den ich selbst kontrolliere. Nichts davon ist auf ein laufendes System gerichtet.
Nach Zugangsdaten im HTML der eigenen Seite suchen
$ curl -s https://example.org/kanzlei/bewertungen/ > /tmp/page.html
# schlüsselförmige Strings, gängige Anbieter-Präfixe
$ grep -oE 'AIza[0-9A-Za-z_-]{35}' /tmp/page.html
$ grep -oiE '(key|api[_-]?key|token|app[_-]?id|client[_-]?id)="?[0-9A-Za-z_-]{20,}' /tmp/page.html | sort -u
$ grep -oE 'pk_(live|test)_[0-9A-Za-z]{20,}' /tmp/page.html
# und die Widget-Konfiguration selbst
$ grep -oiE '(google|maps|places|geocode)[a-z0-9_.-]*["\x27:=\s]+[0-9A-Za-z_-]{20,}' /tmp/page.html | sort -u
Erkennung in eigener Infrastruktur
Prüfen, ob ein Schlüssel wirklich nutzbar und unrestringiert ist
# ein Geocoding-Key ohne Origin-Beschränkung antwortet von überall.
# mit dem EIGENEN Key im EIGENEN Projekt testen:
$ curl -s -o /dev/null -w '%{http_code}\n' \
"https://maps.googleapis.com/maps/api/geocode/json?address=Berlin&key=EIGENER_KEY"
# dieselbe Anfrage mit nicht erlaubtem Origin-Header zeigt, was der Anbieter sieht
$ curl -s -o /dev/null -w '%{http_code}\n' \
-H 'Referer: https://unrelated.example/' \
"https://maps.googleapis.com/maps/api/geocode/json?address=Berlin&key=EIGENER_KEY"
# Kontenlimits und Quota-Warnungen prüfen, bevor ein Vorfall sie braucht
Behebung
Den Schlüssel beim Anbieter einschränken, nicht im Code
# in der Anbieter-Konsole des Schlüssels, nicht im Repository:
# - Anwendungseinschränkungen: HTTP-Referrer
# https://example.org/*
# https://www.example.org/*
# - API-Einschränkungen: nur die Geocoding-API aktivieren
# - Kontenlimit: tägliches Anfragelimit setzen
# - Abrechnung: Ausgaben-Alarm, nicht nur Fehler-Alarm
# wenn der Browser keinen kostenpflichtigen Schlüssel halten darf, über den eigenen Origin proxen
# und das Ergebnis serverseitig cachen
GET /api/geocode?q=Berlin
-> der Server ruft den Anbieter mit einem serverseitig gehaltenen Key
-> Antwort gecacht, der Browser sieht den Key nie
Die Header-Baseline in der Plattformkonfiguration ausliefern
<IfModule mod_headers.c>
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains"
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
</IfModule>
ServerTokens Prod
ServerSignature Off
Sobald ein Angreifer Zugriff hat, kann er folgende Aktionen durchführen:
Fazit — was ich daraus mitnehme
- Ein Key im Client-HTML ist in Ordnung, bis er abrechenbar ist. Prüfen Sie die Anbieter-Einstellungen.
- Per Referrer und API einschränken; ein Geocoding-Key braucht weder Standort noch Schreibzugriff.
- Setzen Sie einen Ausgaben-Alarm. Das erste Signal für Missbrauch sollte keine Rechnung sein.
- Gute und schlechte Entscheidungen koexistieren. Auditieren Sie die letzte Entscheidung, nicht die durchschnittliche.