Header als Sammlung
HTTP-Logging-Frameworks behandeln Request-Header als Sammlung von Schlüssel-Wert-Paaren. Die Logging-API gibt die gesamte Sammlung frei. Operatoren aktivieren Header-Logging zur Fehlersuche: wenn eine Anfrage fehlschlägt, erzählen die Header die Geschichte. Keine eingebaute Deny-List. Keine Filterung von Anmeldedaten in der Dokumentation. Vollständige Header auf die Festplatte.
Die Anmeldedaten-Header in einer typischen Anfrage:
- Authorization: Bearer eyJhbGciOiJIUzI1NiJ9... (JWT- oder OAuth-Token)
- Cookie: session=abc123; auth=xyz789
- X-API-Key: sk-live-abc123...
- X-Auth-Token: ghp_abc123... (GitHub Personal Access Token-Muster)
Diese Werte authentifizieren die Anfrage. In eine Log-Datei geschrieben, authentifizieren sie jede Anfrage.
Die Credential-Pipeline
Ein in eine Log-Datei geschriebenes Credential bleibt nicht an einem Ort. Es wandert:
1. Webserver schreibt nach /var/log/nginx/access.log
2. Log-Rotationsagent (logrotate) kopiert nach /var/log/nginx/access.log.1
3. Log-Shipper (Fluentd, Filebeat, Logstash) liest und versendet an den Aggregator
4. Log-Aggregator (Elasticsearch, Splunk, Datadog) indiziert & speichert
5. 30–90 Tage gemäß Standard-Policy aufbewahrt
Die Zugangsdaten existieren gleichzeitig an allen fünf Orten. Das Widerrufen des Session-Tokens entfernt die Zugangsdaten nicht aus dem Log-Aggregator. Sie bleiben für das gesamte Aufbewahrungsfenster durchsuchbar, exportierbar und für jeden mit Log-Zugriff zugänglich.
Das Exposure-Fenster
Exposure-Fenster für Zugangsdaten im Speicher: max(Session-Dauer, Prozess-Lebensdauer). Session: Stunden bis Tage. Prozess: Stunden bis Wochen.
Exposure-Fenster für Zugangsdaten in Logs: max(Session-Dauer, Log-Retention). Session: Stunden bis Tage. Retention: 30–90 Tage.
Ein aus dem Speicher gestohlener Credential erforderte, dass der Angreifer während des Session-Fensters anwesend war. Ein aus einem Log gestohlener Credential erfordert lediglich Zugriff auf den Log-Aggregator – rückwirkend und für den gesamten Aufbewahrungszeitraum.
MOAD-0003 vs MOAD-0004
MOAD-0003 (Leaked Context): Ein Credential im Speicher wird an den falschen Request-Handler weitergegeben. Nur während des Prozessfensters über den Thread-Pool zugänglich. Ephemeral.
MOAD-0004 (Logged Secret): Ein Credential auf der Festplatte bleibt über Log-Rotation, Log-Shipping und Log-Aggregation erhalten. Nachträglich zugänglich für jeden mit Log-Zugriff, für 30–90 Tage. Persistent.
Der strukturelle Unterschied: ephemeral vs. persistent. Die Behebung erfolgt auf einer anderen Schicht.
Ephemeral vs Persistent
Die Unterscheidung zwischen ephemeral und persistent bestimmt die Risikooberfläche, die Fix-Schicht und die Anforderungen an die Incident Response.
Credential-Denylist auf der Serialisierungsschicht
Die Lösung: eine Credential-Denylist auf der Serialisierungsschicht. Bevor ein Header-Wert die Log-Ausgabe erreicht, wird der Header-Name gegen eine Denylist geprüft. Der Wert wird durch [REDACTED] ersetzt.
CREDENTIAL_HEADERS = {
'authorization',
'cookie',
'x-api-key',
'x-auth-token',
'x-csrf-token',
'proxy-authorization',
}
def sanitize_headers(headers: dict) -> dict:
return {
k: '[REDACTED]' if k.lower() in CREDENTIAL_HEADERS else v
for k, v in headers.items()
}
Die Denylist gehört auf die Serialisierungsebene, nicht auf die Log-Query-Ebene. Log-Query-Redaktion: greift erst, nachdem das Credential das Laufwerk erreicht hat; der Rohwert existiert weiterhin, ist nur vor der Anzeige verborgen. Serialisierungsebenen-Redaktion: das Credential erreicht das Laufwerk nie. Der Rohwert gelangt niemals in die Log-Datei, den Log-Shipper oder den Log-Aggregator.
Testen der Denylist
Drei Testmuster:
- Positiv: eine Anfrage mit Authorization: Bearer token123 erzeugt einen Log-Eintrag mit Authorization: [REDACTED]
- Negativ: Eine Anfrage mit Content-Type: application/json erzeugt einen Log-Eintrag mit dem unveränderten Wert
- Groß-/Kleinschreibung wird ignoriert: AUTHORIZATION: Bearer token123 erzeugt ebenfalls [REDACTED] (HTTP-Header-Namen sind unabhängig von Groß-/Kleinschreibung)
Die Denylist erfordert Wartung: Neue Credential-Header-Muster (z. B. benutzerdefinierte X-Service-Auth-Header) müssen explizit hinzugefügt werden. Die Lösung ist strukturell, aber nicht selbstwartend.
Denylist anwenden
Ein Team konfiguriert das Nginx-Zugriffslog-Format so, dass alle Request-Header zur Fehlersuche bei einem Produktionsvorfall enthalten sind. Die Konfiguration:
log_format debug_format '$remote_addr - $request - $http_authorization - $http_cookie';
access_log /var/log/nginx/debug.log debug_format;
Sie beheben den Vorfall und beabsichtigen, die Debug-Konfiguration zu entfernen, aber die Änderung erreicht die Produktion nicht vor dem nächsten Deployment-Zyklus (7 Tage später).