Cabeçalhos como Bags
Frameworks de logging HTTP tratam os cabeçalhos da requisição como um saco de pares chave-valor. A API de logging expõe o saco completo. Operadores habilitam o logging de cabeçalhos para depuração: quando uma requisição falha, os cabeçalhos contam a história. Sem denylist embutida. Sem filtragem de credenciais na documentação. Cabeçalhos completos para o disco.
Os cabeçalhos de credenciais em uma requisição típica:
- Authorization: Bearer eyJhbGciOiJIUzI1NiJ9... (token JWT ou OAuth)
- Cookie: session=abc123; auth=xyz789
- X-API-Key: sk-live-abc123...
- X-Auth-Token: ghp_abc123... (token de acesso pessoal do GitHub)
Esses valores autenticam a requisição. Quando gravados em um arquivo de log, eles autenticam qualquer requisição.
O Pipeline de Credenciais
Uma credencial gravada em um arquivo de log não permanece em um único lugar. Ela viaja:
1. O servidor web grava em /var/log/nginx/access.log
2. O agente de rotação de logs (logrotate) copia para /var/log/nginx/access.log.1
3. O shipper de logs (Fluentd, Filebeat, Logstash) lê e envia para o agregador
4. Agregador de logs (Elasticsearch, Splunk, Datadog) indexa e armazena
5. Mantido por 30-90 dias conforme política padrão
A credencial existe simultaneamente em todos os cinco locais. Revogar o token de sessão não remove a credencial do agregador de logs. Ela permanece pesquisável, exportável e acessível a qualquer pessoa com acesso aos logs durante todo o período de retenção.
A Janela de Exposição
Janela de exposição para uma credencial na memória: max(duração da sessão, tempo de vida do processo). Sessão: horas a dias. Processo: horas a semanas.
Janela de exposição para uma credencial em um log: max(duração da sessão, retenção do log). Sessão: horas a dias. Retenção: 30-90 dias.
Uma credencial roubada da memória exigia que o atacante estivesse presente durante a janela da sessão. Uma credencial roubada de um log requer apenas acesso ao agregador de logs, disponível retroativamente, durante todo o período de retenção.
MOAD-0003 vs MOAD-0004
MOAD-0003 (Vazamento de Contexto): uma credencial em memória é exposta para o manipulador de requisição incorreto. Acessível apenas durante a janela do processo, por meio do pool de threads. Efêmero.
MOAD-0004 (Segredo Registrado em Log): uma credencial em disco persiste após rotação, envio e agregação de logs. Acessível retroativamente, por qualquer pessoa com acesso aos logs, por 30-90 dias. Persistente.
A diferença estrutural: efêmero versus persistente. A correção atua em uma camada diferente.
Efêmero vs Persistente
A distinção efêmero/persistente determina a superfície de risco, a camada de correção e os requisitos de resposta a incidentes.
Denylist de Credenciais na Camada de Serialização
A correção: uma denylist de credenciais na camada de serialização. Antes que qualquer valor de cabeçalho chegue à saída do log, verifique o nome do cabeçalho contra uma denylist. Substitua o valor por [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()
}
A denylist pertence à camada de serialização, não à camada de consulta de logs. Redação na consulta de logs: aplica-se depois de a credencial ter chegado ao disco; o valor original ainda existe, apenas fica oculto na exibição. Redação na camada de serialização: a credencial nunca chega ao disco. O valor original nunca entra no arquivo de log, no log shipper ou no agregador de logs.
Testando a Denylist
Três padrões de teste:
- Positivo: uma requisição com Authorization: Bearer token123 produz uma entrada de log com Authorization: [REDACTED]
- Negativo: uma requisição com Content-Type: application/json produz uma entrada de log com o valor intacto
- Case-insensitive: AUTHORIZATION: Bearer token123 também produz [REDACTED] (nomes de cabeçalhos HTTP são case-insensitive)
A denylist requer manutenção: novos padrões de cabeçalhos de credenciais (ex.: cabeçalhos personalizados X-Service-Auth) precisam ser adicionados explicitamente. A correção é estrutural, mas não é autossustentável.
Aplicar a Denylist
Uma equipe configura o formato do log de acesso do Nginx para incluir todos os cabeçalhos da requisição durante a depuração de um incidente em produção. A configuração:
log_format debug_format '$remote_addr - $request - $http_authorization - $http_cookie';
access_log /var/log/nginx/debug.log debug_format;
Eles resolvem o incidente e pretendem remover a configuração de debug, mas a alteração não chega à produção antes do próximo ciclo de implantação (7 dias depois).