Intestazioni come Sacche
I framework di logging HTTP trattano le intestazioni delle richieste come una sacca di coppie chiave-valore. L'API di logging espone l'intera sacca. Gli operatori abilitano il logging delle intestazioni per il debug: quando una richiesta fallisce, le intestazioni raccontano la storia. Nessuna denylist integrata. Nessun filtraggio delle credenziali nella documentazione. Intestazioni complete su disco.
Le intestazioni delle credenziali in una richiesta tipica:
- Authorization: Bearer eyJhbGciOiJIUzI1NiJ9... (token JWT o OAuth)
- Cookie: session=abc123; auth=xyz789
- X-API-Key: sk-live-abc123...
- X-Auth-Token: ghp_abc123... (token di accesso personale GitHub)
Questi valori autenticano la richiesta. Se scritti in un file di log, autenticano qualsiasi richiesta.
La pipeline delle credenziali
Una credenziale scritta in un file di log non resta in un unico posto. Viaggia:
1. Il server web scrive in /var/log/nginx/access.log
2. L'agente di rotazione dei log (logrotate) copia in /var/log/nginx/access.log.1
3. Lo shipper dei log (Fluentd, Filebeat, Logstash) legge e invia all'aggregatore
4. Log aggregator (Elasticsearch, Splunk, Datadog) indicizza e archivia
5. Conservato per 30-90 giorni secondo la policy predefinita
La credenziale esiste simultaneamente in tutte e cinque le posizioni. La revoca del token di sessione non rimuove la credenziale dal log aggregator. Rimane ricercabile, esportabile e accessibile a chiunque abbia accesso ai log per l'intero periodo di retention.
La Finestra di Esposizione
Finestra di esposizione per una credenziale in memoria: max(durata sessione, durata processo). Sessione: da ore a giorni. Processo: da ore a settimane.
Finestra di esposizione per una credenziale in un log: max(durata sessione, retention log). Sessione: da ore a giorni. Retention: 30-90 giorni.
Una credenziale rubata dalla memoria richiedeva che l'attaccante fosse presente durante la finestra di sessione. Una credenziale rubata da un log richiede solo l'accesso al log aggregator, disponibile retroattivamente, per l'intero periodo di retention.
MOAD-0003 vs MOAD-0004
MOAD-0003 (Credenziale trapelata): una credenziale in memoria viene esposta all'handler di richiesta errato. Accessibile solo durante la finestra di processo, tramite il thread pool. Effimera.
MOAD-0004 (Segreto registrato): una credenziale su disco persiste attraverso la rotazione, la spedizione e l'aggregazione dei log. Accessibile retroattivamente, a chiunque abbia accesso ai log, per 30-90 giorni. Persistente.
La differenza strutturale: effimero vs persistente. La correzione opera a un livello diverso.
Effimero vs Persistente
La distinzione effimero/persistente determina la superficie di rischio, il livello di correzione e i requisiti di risposta agli incidenti.
Denylist delle credenziali a livello di serializzazione
La correzione: una denylist delle credenziali a livello di serializzazione. Prima che un valore di header raggiunga l’output del log, controlla il nome dell’header contro una denylist. Sostituisci il valore con [REDACTED].
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()
}
La denylist appartiene al livello di serializzazione, non al livello di query dei log. Redaction delle query di log: si applica dopo che la credenziale ha raggiunto il disco; il valore grezzo esiste ancora, è solo nascosto dalla visualizzazione. Redaction al livello di serializzazione: la credenziale non raggiunge mai il disco. Il valore grezzo non entra mai nel file di log, nello shipper dei log o nell'aggregatore di log.
Test della Denylist
Tre pattern di test:
- Positivo: una richiesta con Authorization: Bearer token123 produce una voce di log con Authorization: [REDACTED]
- Negativo: una richiesta con Content-Type: application/json produce una voce di log con il valore intatto
- Case-insensitive: AUTHORIZATION: Bearer token123 produce comunque [REDACTED] (i nomi degli header HTTP sono case-insensitive)
La denylist richiede manutenzione: nuovi pattern di header per credenziali (es. header personalizzati X-Service-Auth) devono essere aggiunti esplicitamente. La correzione è strutturale ma non si mantiene automaticamente.
Applica la Denylist
Un team configura il formato del log di accesso di Nginx per includere tutti gli header delle richieste durante il debug di un incidente in produzione. La configurazione:
log_format debug_format '$remote_addr - $request - $http_authorization - $http_cookie';
access_log /var/log/nginx/debug.log debug_format;
Risolvono l’incidente e intendono rimuovere la configurazione di debug, ma la modifica non raggiunge la produzione prima del successivo ciclo di deployment (7 giorni dopo).