Um caso que desenvolvedores indie sem formação em segurança costumam enfrentar, com todo detalhe identificável removido e transformado em lição. Não há passos de reprodução de ataque aqui. O objetivo é a defesa, para que o mesmo erro não aconteça com você.
- Classe
- Vazamento de chave de API / cobrança fraudulenta
- Severidade
- Crítica (um CVSS 10.0 publicado foi abusado)
- Causa raiz
- Um RCE pré-autenticação no framework web ficou sem correção por meses
- Escopo do vazamento
- Todos os segredos do ambiente
- Escopo da execução
- Permaneceu dentro de um container sem privilégios (o root do host ficou intacto)
- Correção permanente
- ①atualizar para a versão corrigida ②rotacionar todas as chaves ③monitorar CVEs por máquina
“Parou as cobranças” ≠ “resolvido”
Parar a fatura é uma medida temporária. Fechar o caminho do vazamento é uma tarefa separada. Você só termina quando faz as duas coisas.
O que aconteceu (linha do tempo)
Dia 0 — publicado e em operação
Um app criado com a ajuda da IA foi publicado e estava rodando em produção.Um dia — a fatura dispara
A fatura da IA na nuvem salta de repente. Jobs em massa num modelo que jamais seria usado, fazendo um trabalho que não era seu.Investigação — abuso por terceiros
"Nosso app saiu do controle" não explica. Um terceiro estava operando com uma chave roubada.Rastreamento — investigando o caminho do vazamento
"Então, de onde a chave vazou?" Foi aqui que a investigação de fato começou.
Por que não foi pego de início
Na ordem em que os erros foram cometidos — porque os erros são a lição.
Decidiu a causa cedo demais
→ Antes de decidir "culpa minha" ou "culpa deles", olhe primeiro os dados.
Um grep limpo quase encerrou a busca
Esconder o sintoma pareceu corrigir
Um grep limpo não é o sinal de tudo certo
Mesmo sem nenhuma chave em arquivo algum, uma vulnerabilidade pode extrair variáveis de ambiente de um processo em execução. Vazamentos acontecem em tempo de execução (RCE, headers HTTP), não só em arquivos. Veja O que é RCE.
A causa real: um CVSS 10.0 publicado e deixado sem correção
Uma assinatura estranha em logs de acesso antigos logo casou com a inteligência de ameaças. A faixa de versões do framework web em uso tinha um RCE pré-autenticação publicado (CVSS 10.0) que já estava sendo explorado na prática — e ficou sem correção por meses.
- Isso não foi um bug passivo — foi um invasor executando código no servidor e exfiltrando o ambiente.
- Uma coisa limitou o dano: a execução ficou dentro de um container sem privilégios (o root do host não foi tomado). A análise da invasão não encontrou backdoor persistente, minerador nem C2 — o impacto confirmado foi o roubo de segredos.
- Como o banco de dados interno estava acessível, a resposta presumiu que o conteúdo do banco também havia vazado.
Lição: quando algo se comporta de forma estranha, suspeite primeiro de um CVE conhecido. Não presuma que é um bug seu.
Restrinja o vazamento: onde essa chave estava?
Quando o grep não encontra nada, mude a forma de procurar. Liste onde a chave abusada estava escrita, e os caminhos de vazamento candidatos diminuem. Se a chave abusada só existia nas variáveis de ambiente do servidor de produção, por exemplo, suspeite do servidor de produção ou do provedor de hospedagem, não do Git nem do navegador.
| Onde vaza | Causa comum | Como verificar a sua configuração | Como fechar |
|---|---|---|---|
| Histórico do Git / repositórios públicos | Commitar o .env, chaves escritas direto no código | Varra todo o histórico com gitleaks git. Confira os alertas de secret scanning do GitHub | Revogue primeiro, depois bloqueie antes do commit |
| JavaScript enviado ao navegador | Uma chave numa variável NEXT_PUBLIC_, chamar a API direto do navegador | Procure o prefixo da chave na saída do build | Mova a chamada à API para código do lado do servidor |
| Uma pasta web pública | .env ou backups colocados na pasta pública | Abra /.env no seu próprio site e confirme que retorna 404 ou 403 | Mova os arquivos secretos para fora da pasta pública |
| Logs / páginas de erro | Publicar com a saída de debug ativada, registrar headers de requisição como estão | Confira as configurações de debug em produção e se Authorization e similares aparecem nos logs | Desative a saída de debug e mascare segredos nos logs |
| O servidor em execução | Uma vulnerabilidade publicada (como um RCE) deixada sem correção | Compare a versão de fato em execução com a versão corrigida no aviso de segurança | Atualize e depois substitua todos os segredos do ambiente |
| O provedor de hospedagem | Uma invasão do lado do provedor | Confira os boletins de segurança e os avisos por e-mail do provedor | Guarde os segredos de forma que os valores não possam ser vistos depois, e rotacione |
| Sua máquina de desenvolvimento | Um pacote ou extensão malicioso | Verifique se as versões de pacotes ou extensões que você instalou coincidem com um comprometimento publicado | Remova essa versão e substitua os segredos que estavam na máquina |
Este caso foi a linha "o servidor em execução". As quatro linhas de baixo são caminhos que não deixam rastro no seu código nem no Git. O Secrets Management Cheat Sheet da OWASP também alerta que as variáveis de ambiente costumam ser legíveis por outros processos no mesmo ambiente e podem acabar em logs ou dumps. Então, quando uma chave é abusada, trate como vazados todos os segredos guardados no mesmo lugar.
Chaves separadas aceleram a investigação
Se você usa uma chave diferente por serviço e por ambiente (produção vs. desenvolvimento), só a chave abusada já diz em qual linha da tabela você está. Reutilize uma única chave em todo lugar e você terá de suspeitar de todos os locais. Como separar as chaves é explicado em Quando uma chave de API roubada faz sua conta da OpenAI ser banida.
Conte as vulnerabilidades pela versão que de fato está rodando
Ao verificar as outras dependências, "mais 8 dependências vulneráveis" foi reportado. Isso também estava errado. Tinham sido contadas pelo piso no package.json (a faixa com o acento circunflexo ^). As realmente perigosas eram apenas as 2 dependências fixadas que ficaram para trás.
Confira suas próprias versões em execução
Veja as versões de fato resolvidas, no lockfile ou no container em execução.
# npm: see what's actually installed
npm ls next react react-dom
# inside a running container
docker exec <container> npm ls <package>Os números aqui são as versões que de fato estão rodando, não o ^ no package.json.
Como verificar a sua própria configuração
Quer você tenha acabado de notar uma fatura estranha, quer nada tenha acontecido ainda, dá para verificar nesta ordem. Cada passo só olha a sua própria conta e o seu próprio servidor.
Veja qual chave foi usada, e quando
Confira os arquivos enviados ao navegador
NEXT_PUBLIC_ são embutidos no momento do build no JavaScript enviado ao navegador. Depois de um build, procure o prefixo da sua chave na saída, por exemplo grep -rl "sk-" .next/static. Qualquer coisa encontrada ali estava legível para qualquer pessoa.Confirme que os arquivos secretos não são acessíveis pela pasta pública
curl -sI https://example.com/.env contra o seu próprio site (troque example.com pelo seu domínio) e confirme que o status na primeira linha não é 200 (deve ser 404 ou 403). Mais em auditando arquivos secretos em diretórios públicos.Compare a versão em produção com o aviso de segurança
npm ls dentro do container de produção para ver a versão. osv-scanner scan -L pnpm-lock.yaml verifica se o seu lockfile inclui versões com vulnerabilidades conhecidas.Procure segredos nos logs
gitleaks dir --redact na pasta onde ficam os logs do seu app para encontrar chaves escritas nos logs. O --redact mantém os próprios valores encontrados fora da tela.Confira limites e alertas
O que foi feito primeiro (passos reproduzíveis)
Revogar a chave abusada imediatamente
Atualizar o framework para a versão corrigida
Rotacionar todos os segredos
Análise da invasão
Defesa do lado da conta
Prevenção: monitore CVEs automaticamente
Resumindo, este incidente foi "um humano deixou passar um CVSS 10.0 publicado". Verificações manuais sempre deixam algo escapar. O monitoramento automático os detecta.
Varredura de dependências que você pode começar hoje
Grátis para começar — um passo no CI.
# OSV scanner (Google) checks your lockfile
osv-scanner scan -L pnpm-lock.yaml
# On GitHub, enable Dependabot (repo Settings → Security)Este site, seguindo esta própria lição, monitora suas dependências em busca de CVEs — praticamos o que recomendamos.
Erros comuns e como corrigi-los
- Colocar uma chave nova num servidor que continua aberto: o mesmo caminho rouba a chave nova também. Pare a chave abusada na hora, corrija a vulnerabilidade e só então termine de substituir os segredos restantes.
- Tratar um alerta de orçamento como teto: o Google Cloud afirma que um orçamento só com alertas não para automaticamente o uso nem os gastos. Se você quer que pare, acrescente um limite que bloqueie o uso, como o limite rígido mensal da OpenAI ou um limite de gastos de Workspace da Anthropic.
- Colocar uma chave de API de IA num navegador ou app móvel: qualquer valor que chega ao dispositivo do usuário pode ser extraído. Chame a API de IA a partir do seu servidor e faça o navegador chamar o seu servidor.
- Apagar dos arquivos ou do histórico do Git e parar por aí: quem já copiou ainda pode usar. Revogue primeiro, limpe o histórico depois (veja por que credenciais vazadas no GitHub continuam válidas, em inglês).
- Usar a mesma chave em desenvolvimento e produção: você não consegue saber qual lado vazou e precisa parar os dois. Emita uma chave separada por ambiente.
Um exemplo público: variáveis de ambiente vazadas do lado do provedor
Vazamentos que nenhuma busca em arquivos encontra também acontecem fora do seu próprio servidor. Em 19 de abril de 2026, a Vercel divulgou um acesso não autorizado aos seus sistemas internos. Segundo a empresa, o comprometimento de uma ferramenta de IA de terceiros usada por um funcionário levou ao roubo de variáveis de ambiente de alguns clientes que não estavam marcadas como "sensitive" (a configuração que torna um valor impossível de visualizar depois de salvo).
A Vercel orientou os clientes a revisar e rotacionar as variáveis de ambiente não marcadas como sensitive e a conferir os logs de atividade e os deploys recentes. Também afirmou que apagar projetos ou a conta não basta para eliminar o risco.
Clientes nessa situação poderiam procurar no código e no Git o dia todo sem achar rastro de vazamento. A ação é a mesma deste caso: substituir todos os segredos guardados naquele lugar. Os passos estão em o incidente de segurança da Vercel (abril de 2026) (em inglês).
Lições
Erros comuns
- Considerar resolvido em "parou as cobranças"
- Relaxar porque o grep está limpo
- Mascarar o sintoma num proxy e achar que corrigiu
- Contar vulnerabilidades pelo piso do
package.json - Rotacionar só a única chave confirmadamente abusada
Defesa correta
- Tratar a interrupção das cobranças e o fechamento do caminho do vazamento como dois trabalhos separados, e fazer ambos
- Suspeitar também de vazamentos em tempo de execução (RCE, headers)
- Atualizar o framework que causou o problema
- Julgar pela versão de fato em execução
- Substituir todo o env se ele vazar
Resumindo: o pico na fatura era um sintoma, e a causa foi um RCE CVSS 10.0 deixado sem correção. A causa correta não foi encontrada na primeira tentativa; foi encontrada errando várias vezes e verificando de novo.
Leia a seguir
- Depois de um vazamento: 543.699 credenciais expostas no GitHub continuam válidas — por que é preciso revogar, não só apagar (em inglês)
- Glossário: O que é RCE · O que é um CVE · O que é CVSS
- Defesa: Rodando Next.js com segurança (higiene de CVE)
- Incidente: Quando o .env ficou exposto para o mundo inteiro
- Fundamentos: Fundamentos de .env e chaves de API (o que fazer depois de um vazamento)
- Prevenção: Barre segredos antes do commit com o gitleaks
- Incidente: Quando uma chave de API roubada faz sua conta da OpenAI ser banida
Fontes
- OWASP, "Secrets Management Cheat Sheet" (riscos das variáveis de ambiente, resposta a vazamentos): cheatsheetseries.owasp.org
- Documentação do Next.js, "How to use environment variables in Next.js" (embutimento de
NEXT_PUBLIC_): nextjs.org - OSV-Scanner, "Usage": google.github.io
- OpenAI, "Production best practices" (manejo de chaves, alertas e limites): developers.openai.com
- Central de Ajuda do Claude, "Creating and managing Workspaces" (limites e chaves de Workspace): support.claude.com
- Google Cloud, "Create, edit, or delete budgets and budget alerts": docs.cloud.google.com
- Vercel, "Vercel April 2026 security incident": vercel.com