Headers als bags
HTTP logging frameworks behandelen request headers als een bag van key-value pairs. De logging API exposeert de volledige bag. Operators schakelen header logging in voor debugging: wanneer een request faalt, vertellen de headers het verhaal. Geen ingebouwde denylist. Geen credential filtering in de documentatie. Volledige headers naar disk.
De credential headers in een typische request:
- Authorization: Bearer eyJhbGciOiJIUzI1NiJ9... (JWT of OAuth token)
- Cookie: session=abc123; auth=xyz789
- X-API-Key: sk-live-abc123...
- X-Auth-Token: ghp_abc123... (GitHub personal access token patroon)
Deze waarden authenticeren het verzoek. Wanneer ze naar een logbestand worden geschreven, authenticeren ze elk verzoek.
De Credential Pipeline
Een credential die naar een logbestand wordt geschreven, blijft niet op één plek. Het reist:
1. Webserver schrijft naar /var/log/nginx/access.log
2. Logrotatie-agent (logrotate) kopieert naar /var/log/nginx/access.log.1
3. Logshipper (Fluentd, Filebeat, Logstash) leest en verstuurt naar de aggregator
4. Log-aggregator (Elasticsearch, Splunk, Datadog) indexeert & slaat op
5. Bewaard gedurende 30-90 dagen volgens standaardbeleid
De credential bestaat tegelijkertijd op alle vijf locaties. Het intrekken van het sessietoken verwijdert de credential niet uit de log-aggregator. Deze blijft doorzoekbaar, exporteerbaar en toegankelijk voor iedereen met log-toegang gedurende de volledige bewaartermijn.
Het blootstellingsvenster
Blootstellingsvenster voor een credential in het geheugen: max(sessieduur, proceslevensduur). Sessie: uren tot dagen. Proces: uren tot weken.
Blootstellingsvenster voor een credential in een log: max(sessieduur, logbewaartermijn). Sessie: uren tot dagen. Bewaartermijn: 30-90 dagen.
Een credential die uit het geheugen wordt gestolen vereist dat de aanvaller aanwezig is tijdens het sessievenster. Een credential die uit een log wordt gestolen vereist alleen toegang tot de log-aggregator, die met terugwerkende kracht beschikbaar is gedurende de volledige bewaartermijn.
MOAD-0003 vs MOAD-0004
MOAD-0003 (Geheugenlek): een credential in het geheugen lekt naar de verkeerde request handler. Alleen toegankelijk tijdens het procesvenster, via de thread pool. Ephemeral.
MOAD-0004 (Opgeslagen geheim): een credential op schijf blijft bestaan na logrotatie, logverzending en logaggregatie. Retroactief toegankelijk voor iedereen met logtoegang, gedurende 30-90 dagen. Persistent.
Het structurele verschil: ephemeral versus persistent. De oplossing werkt op een andere laag.
Ephemeral vs Persistent
Het onderscheid tussen ephemeral en persistent bepaalt het risicoprofiel, de laag waarop de oplossing moet worden toegepast en de eisen voor incidentrespons.
Credential Denylist op de Serialisatielaag
De fix: een credential-denylist op de serialisatielaag. Voordat een header-waarde de loguitvoer bereikt, controleer de headernaam tegen een denylist. Vervang de waarde door [REDACTED].
CREDENTIAL_HEADERS = {
'authorization',
'cookie',
'x-api-key',
[BLOCK_TYPE SECTION/STEP]
'x-auth-token',
[BLOCK_TYPE SECTION/STEP]
'x-csrf-token',
[BLOCK_TYPE SECTION/STEP]
'proxy-authorization',
[BLOCK_TYPE SECTION/STEP]
}
[BLOCK_TYPE SECTION/STEP]
[BLOCK_TYPE SECTION/STEP]
def sanitize_headers(headers: dict) -> dict:
[BLOCK_TYPE SECTION/STEP]
return {
k: '[REDACTED]' if k.lower() in CREDENTIAL_HEADERS else v
for k, v in headers.items()
}
De denylist hoort thuis op de serialisatielaag, niet op de log-querylaag. Log-query-redactie: wordt toegepast nadat de credential al op schijf is terechtgekomen; de ruwe waarde bestaat nog steeds, maar is alleen verborgen voor weergave. Serialisatielaag-redactie: de credential bereikt nooit de schijf. De ruwe waarde komt nooit in het logbestand, de log-shipper of de log-aggregator terecht.
De denylist testen
Drie testpatronen:
- Positief: een verzoek met Authorization: Bearer token123 produceert een logregel met Authorization: [REDACTED]
- Negatief: een verzoek met Content-Type: application/json produceert een logregel met de waarde intact
- Hoofdletterongevoelig: AUTHORIZATION: Bearer token123 produceert eveneens [REDACTED] (HTTP-headernamen zijn hoofdletterongevoelig)
De denylist vereist onderhoud: nieuwe credential-headerpatronen (bijv. aangepaste X-Service-Auth-headers) moeten expliciet worden toegevoegd. De fix is structureel maar niet zelfonderhoudend.
Pas de Denylist toe
Een team configureert hun Nginx access-logformaat om alle request-headers op te nemen voor het debuggen van een productie-incident. De configuratie:
log_format debug_format '$remote_addr - $request - $http_authorization - $http_cookie';
access_log /var/log/nginx/debug.log debug_format;
Ze lossen het incident op en willen de debug-configuratie verwijderen, maar de wijziging bereikt productie niet vóór de volgende deploymentcyclus (7 dagen later).