Alguém commitou o .env. Ou uma chave de API dentro de um teste. Ou a credencial do banco de produção numa docstring, pra facilitar.
Isso acontece em todo time, incluindo os bons. O que separa um susto de um incidente é o que você faz na primeira hora, e a maior parte das pessoas começa pela ação errada.
A ordem correta
Rotacione primeiro. Sempre.
O instinto é apagar o commit. Reescrever a história, force push, respirar aliviado.
Isso não protege nada. Se o repositório é público, assuma que já foi lido: existem bots varrendo o GitHub em tempo real, e o tempo entre um segredo aparecer e ser usado se mede em minutos, não em dias. Se é privado, o segredo está no clone local de todo mundo do time, no cache do CI, no espelho de backup e possivelmente no índice de alguma ferramenta de busca de código.
Remover do Git não invalida credencial. Só rotacionar invalida.
Então a ordem é: gerar credencial nova, atualizar onde ela é usada, revogar a antiga, confirmar que a antiga não funciona mais. Depois, e só depois, cuidar do histórico.
O que rotacionar, em ordem de urgência
Nem todo segredo tem o mesmo peso.
Chave de provedor de nuvem com permissão ampla vem primeiro. É a que permite escalar pra tudo mais.
Credencial de banco de produção vem junto.
Chave de API de serviço que gasta dinheiro. Provedor de e-mail, de SMS, de LLM. O prejuízo aqui é direto e rápido.
Segredo de assinatura de JWT tem uma particularidade: rotacionar derruba a sessão de todo mundo, e se o sistema não tiver refresh token funcionando, todo usuário é deslogado. Vale saber disso antes, não durante.
Webhook secret e credencial de serviço interno vêm depois.
Como saber se foi usado
Rotacionar fecha a porta. Não diz se alguém entrou.
Puxe o log de uso da credencial antiga no período entre o commit e a rotação. Toda plataforma séria tem isso: CloudTrail na AWS, log de acesso do provedor, histórico de requisição da API.
Procure por origem que não seja a sua infraestrutura, horário fora do padrão, e volume anômalo. Se encontrar qualquer coisa, deixou de ser correção de erro e virou resposta a incidente, com escopo diferente e possivelmente com obrigação de notificar.
Se não encontrar nada, registre isso. “Verificamos o log entre X e Y e não houve uso não autorizado” é uma frase que vale muito num relatório futuro.
Limpar o histórico
Depois da rotação. Com git filter-repo, que substituiu o antigo filter-branch:
git filter-repo --path .env --invert-paths --force
git push origin --force --all
git push origin --force --tags
Três coisas que quase sempre são esquecidas:
Todo clone local precisa ser refeito. Se alguém do time der push a partir de um clone antigo, o commit volta.
Pull request fechado no GitHub mantém o diff acessível mesmo depois do force push. É preciso abrir chamado no suporte pra apagar de verdade.
Fork e espelho não são afetados. Se o repositório tem fork, o commit continua lá.
Por isso a rotação é a parte que importa: a limpeza do histórico é higiene, e nunca é completa.
Prevenir, que é mais barato
Duas medidas cobrem quase tudo, e as duas levam menos de uma hora pra configurar.
Scanner no pre-commit. Gitleaks ou trufflehog num hook local:
# .pre-commit-config.yaml
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.28.0
hooks:
- id: gitleaks
Hook local é contornável e pega o caso do descuido honesto, que é 95% dos casos.
A mesma verificação no CI, falhando o build. Essa não é contornável, e é a que realmente segura.
Some a isso um .gitignore que já cubra .env, .env.* com exceção do .env.example, *.pem, *.key e a pasta de credencial da sua nuvem.
A causa raiz de verdade
Se o segredo foi commitado, é porque estava em arquivo. E estar em arquivo é o modelo antigo.
O caminho que resolve a categoria inteira é o segredo nunca tocar o disco do desenvolvedor: gerenciador de segredo injetando variável de ambiente no momento da execução. Existem várias opções boas e a escolha importa menos que a mudança de modelo.
Enquanto o fluxo normal for “peça o .env pro colega no chat”, o incidente vai se repetir. Não por falta de cuidado: porque o processo pede que a credencial fique em texto puro na máquina de todo mundo, e uma hora uma delas escapa.
O que não fazer
Não esconda do time. Todo mundo precisa saber pra não dar push de um clone velho e pra ficar atento a comportamento estranho.
Não culpe quem commitou. Se o processo permite que aconteça, o processo é o problema. Culpar garante que a próxima pessoa esconda em vez de avisar, e segredo vazado que ninguém reporta é o pior cenário possível.
E não pule a rotação porque “o repositório é privado”. Privado hoje.
Sobre o resto do básico de segurança, escrevi em como construir aplicações seguras.