~/blog/api-key-exposed-in-client-htmlPOSTED
von Shady Nathan Tawfik · 26. September 2026 · 4 min

Ein kostenpflichtiger API-Schlüssel stand in der Seite, und er funktionierte

Eine Kanzleiseite mit einem Bewertungs-Widget, das einen kostenpflichtigen Geocoding-Schlüssel in das HTML rendert. Der Befund ist schmal, trivial automatisierbar und die profitabelste Position im Bericht.

Interaktiv[blog-2.0]

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.

TL;DR: Ein Bewertungs-Widget rendert einen kostenpflichtigen Geocoding-Schlüssel ins öffentliche HTML. Ich habe verifiziert, dass der Key live ist, gezeigt, wie jeder Besucher die Quota verbrennen kann, und per Referrer-Restriktion plus Server-Proxy behoben.

Befunde

SchweregradBefundNachweis
HOCHKostenpflichtiger Drittanbieter-API-Schlüssel im öffentlichen HTML gerendertVon jedem Besucher lesbar und außerhalb der Site nutzbar
MITTELKeine Sicherheits-Header auf der öffentlichen OberflächeHSTS, Frameschutz und Content-Type-Schutz fehlen
MITTELKeine Enforcement-Policy für die AbsenderdomainAuthentifizierung für die Mail-Domain fehlt
MITTELBenutzer-Enumeration über zwei unabhängige PfadeAutorenkennung und Slug ableitbar
NIEDRIGSehr lange ungepflegtes Plugin weiterhin installiertJahre ohne Release, weiterhin aktiv
INFOLogin und Admin hinter Basic-AuthentifizierungWirksam gegen automatisierte Scans

Der Angriffspfad

Review widget configuredBillable key embedded in pageAny visitor can read the keyKey usable from an attacker originQuota and cost consumedKey should be restricted or proxied

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

~/code/bash bash
$ 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

~/code/bash bash
# 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

~/code/text text
# 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

~/code/apache apache
<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:

Quota verbrennen: Kosten für die Kanzlei
Widget-Funktion lahmlegen (Karten, Bewertungen)
Bei Places-Scopes: Kundendaten abfragen

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.

Nur zu Bildungszwecken. Alle reproduzierbaren Befehle zielen auf meine eigene Lab-Umgebung (localhost), nie auf ein laufendes System. © 2026