Cenário construído para estudo, não o incidente de uma empresa específica. A forma se repete com frequência desconfortável, e é útil justamente porque não envolve nenhum atacante.
Como começa
Uma integração com parceiro começa a falhar de forma intermitente. O payload chega errado às vezes, e ninguém consegue reproduzir.
Alguém adiciona uma linha pra investigar:
this.logger.debug(`Payload recebido: ${JSON.stringify(req.body)}`);
Faz sentido. O bug é intermitente, precisa ver o dado real, e debug não sai em produção mesmo.
Só que o nível de log em produção estava em debug desde uma investigação anterior, e ninguém voltou pra info.
O bug foi resolvido em três dias. A linha ficou.
O que ela mandava
O corpo daquela requisição continha nome completo, CPF, data de nascimento, endereço e telefone. Cerca de quatro mil requisições por dia.
O destino: o provedor de log, com retenção de 90 dias, acessível a todo mundo do time de engenharia, do produto e do suporte. Vinte e três pessoas.
Sete meses, aproximadamente oitocentas mil linhas com dado pessoal, num sistema que nunca foi tratado como repositório de dado sensível.
Por que ninguém viu
Três razões que se combinam.
Log não é revisado. Ninguém abre a ferramenta de log pra ler o que está sendo gravado. Abre-se pra buscar um erro específico, com filtro, e o que aparece é só a linha procurada.
A revisão de código olhou lógica. A linha adicionada era uma linha de debug num PR que resolvia um bug real. Ninguém pensa em classificação de dado ao aprovar um logger.debug.
E a ferramenta de log estava fora do inventário de sistemas que tratam dado pessoal. O mapeamento de dado da empresa listava banco, backup e o data warehouse. Log era considerado infraestrutura.
Essa terceira é a mais interessante, porque é conceitual. Log é tratado como diagnóstico, e ele é armazenamento de dado como qualquer outro.
Como se descobre
Do jeito mais comum: alguém do suporte precisou de um dado, foi procurar no log e comentou “que legal, dá pra ver o CPF do cliente aqui”.
Não houve invasão. Não houve vazamento externo. E ainda assim, sob a LGPD, isso é tratamento de dado pessoal sem base legal, sem controle de acesso adequado e sem previsão no mapeamento. É incidente, com obrigação de registro e avaliação de comunicação.
A correção imediata
Remover a linha e reduzir o nível de log fecha a torneira e não limpa o que já entrou. Os passos que importam:
Purgar os índices antigos no provedor, o que costuma exigir chamado no suporte deles.
Verificar quem acessou aquilo, se a ferramenta guardar log de acesso.
Registrar o incidente, com prazo, escopo e avaliação de risco.
E a parte que dá mais trabalho: descobrir se existem outros lugares fazendo o mesmo.
grep -rn "JSON.stringify(req" src/
grep -rn "logger\.\w*(.*body" src/
grep -rn "console\.log" src/
Nesse tipo de varredura quase sempre aparecem mais dois ou três casos que ninguém sabia.
A correção estrutural
Remover a linha resolve uma linha. O que impede a próxima:
Redação centralizada. Uma lista de campos que nunca são gravados, aplicada no interceptor de log, não na memória de quem escreve:
const REDACT = new Set([
"password", "senha", "cpf", "cnpj", "token", "authorization",
"card_number", "cvv", "birth_date", "rg",
]);
function redact(value: unknown): unknown {
if (Array.isArray(value)) return value.map(redact);
if (value && typeof value === "object") {
return Object.fromEntries(
Object.entries(value).map(([k, v]) =>
REDACT.has(k.toLowerCase()) ? [k, "[REDACTED]"] : [k, redact(v)]
)
);
}
return value;
}
Isso não é perfeito. Campo com nome inesperado passa. E cobre a esmagadora maioria dos casos com custo baixo.
Proibir log de objeto inteiro. Regra de lint que rejeita JSON.stringify de req.body, req.headers e afins dentro de chamada de log. A máquina verifica em toda linha nova, inclusive nas escritas durante um incidente.
Detector no pipeline. Um job que roda contra uma amostra dos logs procurando padrão de CPF, cartão e e-mail, e alerta quando encontrar. É a rede que pega o que passou pelas duas primeiras.
Log no inventário de dado. A mudança mais barata e a mais importante. Enquanto a ferramenta de log não estiver na lista de sistemas que tratam dado pessoal, ela não tem dono, não tem retenção definida e não tem controle de acesso revisado.
O aprendizado
Vazamento não precisa de invasor. Precisa de uma linha de debug e de sete meses de ninguém olhar.
E a defesa que funciona não é treinar as pessoas a não logar dado sensível. Elas já sabem. A defesa é fazer com que logar dado sensível exija esforço deliberado, o que só acontece quando a redação é padrão e a exceção é que precisa ser escrita.
Sobre a família de problemas em que isso se encaixa, escrevi em como construir aplicações seguras.