Remover segredos do histórico do Git após um vazamento de protótipo de IA
Aprenda a remover segredos do histórico do Git usando git filter-repo ou BFG, rotacionar chaves vazadas e confirmar que seu repositório está limpo antes de enviar.

O que realmente significa um segredo vazado no Git
Um "segredo" é qualquer coisa que concede acesso se outra pessoa a obtiver: chaves de API, tokens de acesso, senhas, strings de conexão de banco de dados, certificados de assinatura e chaves privadas. Se algo pode fazer login, cobrar um cartão, ler dados ou implantar código, trate como um segredo.
Quando um segredo é commitado no Git, ele passa a fazer parte do histórico do repositório. Deletar o arquivo depois (ou adicioná-lo ao .gitignore) não remove o commit anterior que ainda contém o valor. Qualquer um com acesso ao repositório, a um fork, a um clone antigo ou a uma cópia em cache ainda pode encontrá-lo. Por isso remover segredos do histórico é um trabalho separado de simplesmente apagar o arquivo.
Protótipos construídos por IA vazam segredos de formas previsíveis. Um padrão comum é commitar um arquivo .env durante a configuração inicial e esquecer dele. Outro é colar um trecho funcional do painel de um provedor no código "só para testar". Logs de depuração podem ser tão perigosos quanto se imprimirem tokens e forem commitados junto com o resto.
O que está em risco vai além do constrangimento: acesso não autorizado a serviços (bancos de dados, armazenamento, provedores de autenticação), cobranças inesperadas por APIs abusadas, exposição ou modificação de dados de clientes e perda de confiança se usuários ou parceiros ficarem sabendo.
Um exemplo concreto: um protótipo usa uma chave Stripe real, commita-a uma vez e depois troca por uma nova chave em um commit posterior. Mesmo que o código atual pareça correto, a primeira chave ainda está no histórico, esperando para ser copiada.
Pare o vazamento primeiro
No momento em que um segredo cai no histórico do Git, trate-o como exposto. Mesmo que o repositório fosse privado, ele pode já ter sido copiado por um clone de um colega, um runner de CI, um cache de build ou uma ferramenta de IA que puxou o código. Não espere confirmar. Aja como se um atacante já o tivesse.
Objetivo inicial: tornar a chave vazada inútil. Revogue, desative ou apague-a o mais rápido possível. Se o provedor permitir limitar o escopo, reduza permissões imediatamente como medida temporária, mas planeje rotacionar totalmente a credencial após a limpeza.
Em seguida, pare qualquer coisa que possa continuar usando ou espalhando o segredo. Pause jobs de CI, fluxos agendados, ambientes de preview e deploys automáticos. Esses normalmente buscam variáveis de ambiente e geram logs que podem vazar valores novamente.
Antes de mexer no histórico do Git, alinhe a equipe:
- Revogue ou desative a credencial exposta.
- Pause CI/deploys e avise a todos para não fazer push, merge ou tag até o plano ficar claro.
- Capture o valor exato e onde ele apareceu (nome do arquivo e hash do commit) para que você remova a coisa certa.
- Mantenha uma nota de incidente privada: o que foi rotacionado, quando e quais serviços podem ser afetados.
Exemplo: se seu protótipo comitou uma chave Stripe e o CI roda testes a cada push, a pipeline pode continuar chamando Stripe com a chave vazada e deixar rastros nos logs. Pause a pipeline, revogue a chave e depois passe para reescrever o histórico.
Inventário do que precisa ser purgado
Antes de reescrever o histórico, seja específico sobre o que vazou e onde aparece. Assim você evita uma "limpeza" que perde o problema real.
Anote cada valor vazado que você conhece: chaves de API, URLs de banco de dados, segredos JWT, segredos de cliente OAuth, tokens de webhook. Se você só tem uma captura de tela ou um log de erro, extraia o texto exato se puder.
Depois mapeie cada segredo para suas localizações no repositório. Não olhe apenas para a branch atual. Segredos costumam viver em commits antigos, arquivos de teste, saída de depuração, dados exportados e até capturas de tela.
Lugares comuns para checar incluem variantes de .env, arquivos de configuração (como settings.json ou docker-compose.yml), pastas de logs e tmp, dados seed/sample e docs/assets que possam conter screenshots.
Uma forma rápida de buscar uma string conhecida na sua cópia de trabalho:
git grep -n "PASTE_PART_OF_KEY_HERE" -- .
Decida o que precisa remover:
- Strings exatas (melhor quando um token aparece em vários lugares)
- Arquivos inteiros (melhor para
.envou dados exportados que nunca deveriam estar no Git) - Pastas inteiras (melhor para
logs/,tmp/, backups acidentais)
Exemplo: um protótipo gerado por IA pode ter comitado .env e também colado uma URL de banco de produção em config.ts. São dois alvos: remover .env de todo o histórico e remover a string da URL onde quer que ela apareça.
Por fim, faça um backup seguro do repositório antes de reescrever o histórico. Copie a pasta inteira ou crie um clone mirror para poder recuperar caso a reescrita dê errado.
Passo a passo: purgar com git filter-repo
Se você precisa de reescritas flexíveis e confiáveis (múltiplas branches, tags, histórico estranho ou mais de um segredo), git filter-repo normalmente é a melhor escolha.
Comece a partir de um clone fresco e faça uma cópia de segurança. Feche PRs abertos que possam reintroduzir commits antigos.
1) Remover um arquivo de todo lugar (exemplo: um arquivo env)
Se um protótipo comitou por acidente .env (ou um JSON de credenciais), purge pelo caminho:
git filter-repo --force --invert-paths --path .env
Isso reescreve cada commit e remove o arquivo onde quer que ele tenha aparecido.
2) Remover ou substituir strings secretas por conteúdo
Quando segredos estão embutidos no código, use um arquivo de substituições. Crie replacements.txt assim (lado esquerdo é o que casar, lado direito é o que escrever):
AKIAIOSFODNN7EXAMPLE==\u003eREMOVED
\"api_key\": \"sk-live-123\"==\u003e\"api_key\": \"REMOVED\"
Então rode:
git filter-repo --force --replace-text replacements.txt
Antes de reescrever, decida quais branches e tags você incluirá. Por padrão, filter-repo reescreve o que está no seu clone local, então faça fetch de tudo que planeja manter (incluindo tags) e apague branches que não quer publicar.
Depois da reescrita, confirme que nada quebrou localmente. Rode seu build e testes, inicie o servidor e faça uma checagem rápida end-to-end (por exemplo, um fluxo básico de login). Então busque de novo por padrões da chave antiga.
Passo a passo: purgar com BFG Repo-Cleaner
BFG Repo-Cleaner é uma boa opção quando você quer uma limpeza rápida, especialmente para deletar arquivos inteiros como .env ou creds.json, ou trocar strings conhecidas por outras.
Antes de começar, faça um mirror clone para reescrever todas as refs (branches e tags) com segurança:
# 1) Mirror clone (works best for history rewrites)
git clone --mirror <your-repo-url> repo.git
cd repo.git
# 2) Remove whole files everywhere in history
bfg --delete-files .env
bfg --delete-files creds.json
# 3) Or replace leaked text (use a rules file)
# lines like: OLD_SECRET==\u003eREMOVED
bfg --replace-text replacements.txt
# 4) Prune old objects BFG made unreachable
git reflog expire --expire=now --all
git gc --prune=now --aggressive
BFG reescreve commits que continham o segredo, criando um novo histórico onde esses blobs ou strings desapareceram. Ele não rotaciona credenciais e não te protege de cópias do repositório que alguém já clonou.
Valide o resultado antes de dar push em qualquer coisa. Busque pelos tokens exatos que você espera que tenham sumido, verifique que arquivos sensíveis não existem em nenhum commit e inspecione tags também.
Envie o histórico reescrito sem causar caos
Depois de reescrever o histórico, o push é onde as equipes costumam se surpreender. O objetivo é simples: publicar o histórico limpo e impedir que alguém reintroduza os commits antigos por acidente.
Coordene um congelamento curto (15 a 60 minutos) onde ninguém faz push, merge ou abre novas branches. Decida o que realmente precisa ser reescrito: normalmente main mais quaisquer branches de longa duração que as pessoas realmente usem. Branches antigas de feature muitas vezes podem ser deletadas em vez de reescritas.
Uma sequência segura:
- Anuncie o congelamento e confirme que todos estão pausados.
- Relaxe temporariamente regras de branches protegidas se elas bloquearem force pushes.
- Force-push as branches reescritas (e as tags, se preciso).
- Reative as proteções de branch imediatamente.
- Diga a todos exatamente como sincronizar seus clones locais.
Porque os IDs de commit mudaram, normalmente será necessário um force push. Dê aos colaboradores uma de duas opções: re-clonar (mais simples) ou dar um hard reset (mais rápido, mas fácil de errar). Por exemplo:
git fetch --all --prune
git checkout main
git reset --hard origin/main
Também limpe os lugares onde segredos gostam de permanecer: clones locais antigos, caches de CI, artefatos de build e repositórios espelhados.
Rotacione credenciais do jeito seguro
Uma vez que um segredo foi commitado, trate-o como comprometido. Rotacione cada credencial vazada que você conseguir encontrar, não apenas aquela que disparou o alarme.
Liste qualquer coisa que possa conceder acesso: chaves de API, senhas de banco, arquivos de conta de serviço, segredos de cliente OAuth, chaves de assinatura JWT, credenciais SMTP, segredos de webhook e quaisquer chaves "de teste" que apontem para sistemas reais. Se o protótipo tocou produção em algum momento, inclua produção no plano de rotação.
Crie novas credenciais com princípio do menor privilégio. Prefira tokens com escopo e vida curta quando o provedor suportar.
Uma ordem segura:
- Crie novos segredos primeiro e então faça deploy deles em apps e CI.
- Atualize produção, staging e configurações locais.
- Confirme que a aplicação funciona end-to-end.
- Revogue segredos antigos por último e monitore por erros.
Depois da rotação, mantenha segredos fora do repositório para sempre. Use variáveis de plataforma de hospedagem ou um cofre de segredos adequado, e mantenha segredos locais em um arquivo env não commitado. Adicione proteções como varredura de segredos em pre-commit e checagens no CI.
Mantenha um registro simples do que mudou, quando, onde o novo valor está guardado e quem é o responsável.
Verifique que o repositório está realmente limpo
Assuma que você não terminou até provar que o vazamento sumiu em todos os lugares que um desenvolvedor poderia buscar.
Busque pelo valor vazado exato, não apenas pelo nome do arquivo. Execute a busca em todos os refs (branches e tags), não só na branch atual.
git fetch --all --tags --prune
git grep -n "PASTE_LEAKED_VALUE_HERE" $(git rev-list --all)
Depois faça uma varredura mais ampla por formatos comuns de segredos. Procure por chaves privadas (BEGIN PRIVATE KEY), strings longas parecidas com base64, JWTs (três pedaços separados por pontos) e prefixos específicos de provedores que você reconheça.
Não esqueça dos logs de CI e artefatos de build. Mesmo que o repositório esteja limpo, um segredo pode ainda estar armazenado em logs de CI, camadas Docker em cache ou artefatos enviados. Reexecute a última pipeline após rotacionar as credenciais para garantir que os logs não imprimem mais valores sensíveis.
Finalmente, faça um teste de clone fresco a partir do remoto em uma nova pasta ou máquina limpa. Um repositório pode parecer limpo localmente enquanto um ref remoto antigo ainda expõe o segredo.
mkdir /tmp/clean-test && cd /tmp/clean-test
git clone <your-remote>
cd <repo>
git log --all -S "PASTE_LEAKED_VALUE_HERE" --oneline
Se você continuar encontrando rastros depois de uma reescrita, normalmente significa que uma tag, branch remota ou artefato de CI ainda está segurando o conteúdo antigo.
Erros comuns que fazem os segredos voltarem
A maioria dos vazamentos volta porque a limpeza foi só pela metade. Deletar um arquivo no commit mais recente não é o mesmo que removê-lo do histórico. Se alguém ainda pode dar checkout em um commit antigo, o segredo continua exposto.
Dois esquecimentos comuns:
- Esquecer tags e branches antigas. Um segredo pode sumir do
mainmas ainda viver em uma tag de release ou numa branch obsoleta. - Remover um arquivo mas não o valor. Protótipos de IA frequentemente copiam a mesma chave para vários lugares: arquivos de config, fixtures de teste, scripts de build e logs.
Outra causa frequente é um colega fazer push a partir de um clone antigo depois da reescrita. Coordene um momento para que todos re-clonem ou façam reset, e bloqueie merges até que isso esteja feito.
Exemplo: vazamento em protótipo de IA e um plano realista de limpeza
Uma história comum: um fundador monta um protótipo rápido em Lovable ou Replit, commita tudo sem notar que um .env entrou. O demo funciona e então algo parece errado.
O que aparece primeiro costuma ser consequências no mundo real: aumento inesperado da fatura na nuvem, alertas de login estranhos de um provedor de e-mail ou um dashboard de API mostrando tráfego de países que você não atende.
Se o repositório é público, assuma que as chaves já foram copiadas. Rotacione as credenciais imediatamente e depois reescreva o histórico do Git para remover o arquivo e quaisquer tokens colados. Se o repositório for privado, faça o mesmo trabalho e também revise quem teve acesso (contratados antigos, máquinas compartilhadas, logs de CI).
Se você não sabe qual chave vazou, aja como se todas tivessem vazado. Liste cada sistema que o protótipo tocou (banco de dados, provedor de autenticação, pagamentos, e-mail, armazenamento) e rotacione em uma ordem que não te tranque para fora.
Um plano realista:
- Congele deploys e pause CI para que nada continue espalhando o valor.
- Rotacione as credenciais mais perigosas primeiro (admin da nuvem, banco, chaves de pagamento).
- Reescreva o histórico para remover
.enve quaisquer tokens commitados, depois force-push. - Atualize o app para ler segredos só de fontes seguras (variáveis de ambiente ou um gerenciador de segredos).
- Verifique o resultado com um clone fresco e buscas em todo o histórico.
A comunicação importa. Diga aos stakeholders o que aconteceu em termos simples, o que foi exposto, o que você rotacionou e o que verificou. Forneça uma linha do tempo curta e uma nota clara sobre o que muda para eles.
Checklist rápido antes de retomar o desenvolvimento
Após um vazamento, o objetivo é direto: tornar o segredo roubado inútil, apagá-lo do histórico e provar que ele sumiu antes de escrever novo código.
- Revogue primeiro, depois pause o trabalho. Desabilite a chave/token vazado imediatamente e pare pushes durante a limpeza.
- Faça inventário do que vazou. Liste cada nome de arquivo e padrão que possa conter credenciais.
- Reescreva o histórico uma vez, com uma ferramenta. Escolha
git filter-repoou BFG e rode em um clone fresco. - Envie o novo histórico e resete colaboradores. Force-push branches/tags reescritas, então peça que todos re-clonem (ou façam hard reset).
- Rotacione e realoque. Crie novas credenciais, mova segredos para fora do repositório e adicione checagens para evitar repetições.
Faça uma última prova: clone em uma pasta nova e escaneie esse clone fresco. Se o scan estiver limpo e a app rodar com as novas credenciais, você voltou para uma linha de base segura.
Próximos passos se o repositório está bagunçado ou o app já está quebrado
Às vezes o problema é maior que uma reescrita de histórico. Se o repositório está emaranhado, o build falha ou você nem sabe o que vazou, é fácil perder dias com cirurgia no Git enquanto o app fica instável.
Sinais de que é hora de pedir ajuda: múltiplos repositórios e forks que você não consegue rastrear, segredos duplicados em arquivos gerados, deploys falhando após a reescrita, vazamentos múltiplos suspeitos ou uma base de código difícil de mudar com segurança (estrutura confusa, sem testes, edições aleatórias por IA).
Se você herdou um protótipo gerado por IA, é exatamente para isso que FixMyMess (fixmymess.ai) existe: diagnosticar o código e o histórico, reparar problemas de lógica e segurança (como autenticação quebrada ou segredos expostos) e deixar o app pronto para deploy limpo. Eles oferecem uma auditoria de código gratuita para mapear as exposições antes de você se comprometer com mudanças, e a maior parte da remediação costuma ser concluída em 48-72 horas.
Perguntas Frequentes
Se eu deletei o arquivo `.env`, por que o segredo ainda está “vazado”?
Significa que o segredo agora faz parte do histórico do repositório, não apenas dos arquivos atuais. Mesmo que você delete o arquivo ou o adicione ao .gitignore, qualquer pessoa que consiga acessar um commit antigo, um fork ou um clone anterior ainda pode recuperar o valor.
O que devo fazer primeiro quando descobrir um segredo no histórico do Git?
Revogue ou desative a credencial exposta imediatamente para que o valor vazado pare de funcionar. Depois, pause CI/deploys que possam continuar usando ou registrando esse valor e só então reescreva o histórico do Git para purgar o segredo dos commits antigos.
Ainda é perigoso se o repositório era privado?
Sim. Repositórios privados ainda são copiados por clones de colegas, runners de CI, caches de build e máquinas compartilhadas. Considere qualquer segredo comprometido e rode a rotação mesmo que o acesso pareça limitado.
Devo usar `git filter-repo` ou BFG Repo-Cleaner?
git filter-repo é, na maioria dos casos, a opção mais segura porque é flexível para múltiplas branches, tags e históricos complicados. BFG costuma ser mais rápido e simples quando você só precisa deletar arquivos inteiros ou fazer substituições diretas de texto, mas você ainda precisa validar tags e executar os passos de limpeza depois.
Por que preciso forçar o push após limpar o repositório?
Porque reescrever o histórico altera os IDs dos commits. Para publicar o histórico limpo você normalmente precisa forçar o push. Planeje uma janela curta de congelamento, envie as branches reescritas uma vez e então faça com que todos re-clonem ou façam um hard reset para evitar que os commits antigos sejam reenviados.
Como posso confirmar que o segredo realmente sumiu depois da reescrita?
Busque em todos os refs, não apenas na branch atual, e procure pelo valor exato vazado além de padrões comuns de segredos. Em seguida, faça um clone fresco em uma pasta nova e busque de novo; um clone novo é a maneira mais simples de detectar refs remotos ou tags que ainda contenham o segredo.
Se eu rotacionar a chave, ainda preciso removê-la do histórico do Git?
Rotacionar torna o valor roubado inútil; limpar o Git impede descobertas futuras e reuso acidental, mas não desfaz exposições já feitas. Portanto, faça ambos: rotacione e limpe o histórico.
O que geralmente faz os segredos voltarem depois de uma limpeza?
O erro mais comum é limpar apenas o main e esquecer tags ou branches antigas que ainda referenciam o commit antigo. Outro problema frequente é um colega enviar de um clone desatualizado após a reescrita, reintroduzindo o segredo. Coordene para que todos re-clonem ou resetem ao mesmo tempo.
Como evito que protótipos gerados por IA virem a vazar segredos de novo?
Não cometa .env no repositório; carregue segredos via variáveis de ambiente ou um gerenciador de segredos da sua plataforma de hospedagem. Adicione varreduras de segredos em pre-commit e checagens no CI para evitar que um token colado ou um log de depuração entre num commit.
O FixMyMess pode ajudar se o repositório estiver emaranhado ou eu tiver medo de quebrar coisas?
Sim. Se o código estiver bagunçado, builds falhando ou segredos duplicados em arquivos gerados, uma reescrita de histórico pode ser arriscada e demorada. FixMyMess pode auditar o repositório, identificar exposições, limpar o histórico com segurança, rotacionar credenciais sem quebrar deploys e deixar o app pronto para deploy rapidamente.