Noções de segurança
Sua chave de API vazou. A ordem em que agir
Uma chave de API vazou e você não sabe o que fazer primeiro. Nem toda chave no seu frontend é um vazamento, e a ordem importa mais que a pressa.

Em resumo
- Se uma chave de API vazou, o que fazer primeiro depende de qual chave é. Chaves publicáveis moram no seu frontend e não precisam de nada.
- Se for um segredo de verdade, descubra se a chave pode gastar dinheiro. É a única parte disso com um relógio correndo.
- Parar uma chave e substituir uma chave são dois controles diferentes em todo provedor, e quase todos dão uma janela em que as duas chaves funcionam.
- Depois leia o log do provedor para o período em que a chave esteve fora, e deixe seu histórico do Git quieto até a chave estar morta.
O aviso costuma chegar por um relatório de varredura, pelo GitHub avisando que apareceu um segredo em um dos seus repositórios, ou por alguém que apertou F12 no seu site e mandou um print. Por onde quer que tenha chegado, você já sabe que uma chave de API sua vazou e não sabe o que fazer primeiro.
Esta é a parte que guia após guia erra: eles dão a mesma resposta para qualquer chave, e a resposta é sempre rotacionar. Algumas dessas chaves deveriam mesmo ser públicas e não precisam de nada. Entre as outras, algumas estão engordando uma fatura enquanto você lê isto, e outras fizeram todo o estrago no dia do deploy. São três situações diferentes, e o primeiro passo é outro em cada uma.
Primeiro: essa chave é mesmo um segredo?
Muitas vezes não, e os números são desequilibrados o bastante para dizer isso com todas as letras.
Varremos 31.056 aplicativos no ar e lemos o JavaScript que cada um entrega ao navegador. A verificação que procura credenciais conseguiu responder em todos, e 1.332 voltaram com pelo menos uma chave que merecia aviso. 1.142 delas eram chaves de API do Google, que é o único tipo do grupo que normalmente está exatamente onde deveria estar.
Uma chave de API do Google numa página web é o que faz o Google Maps funcionar. A chave diz qual projeto paga, e o que impede um estranho de gastar por sua conta é a restrição colocada na chave, e não o sigilo dela. A orientação do próprio Google é direta nas duas metades: "Unrestricted API keys are insecure", e "You are financially responsible for charges caused by abuse of unrestricted API keys." O conserto para uma chave dessas é abri-la no Cloud Console e acrescentar duas restrições, uma nomeando o seu site e outra nomeando as APIs que você realmente chama. A chave em si nunca muda.
A outra família que mora no navegador são as chaves publicáveis. Uma chave da
Stripe pk_live_, e uma do Supabase sb_publishable_ ou anon, foram feitas
para ser lidas por cada visitante que você tem.
Quais chaves são seguras no seu frontend
é a versão de quatro caracteres desse teste.
Se você preferir não percorrer o bundle na mão, a nossa varredura gratuita lê o seu site no ar, lista as chaves que enxerga de fora e diz de que tipo é cada uma: escaneie seu aplicativo. Leva uns 20 segundos e não pede conta.
Minha chave de API vazou. O que faço primeiro?
Descubra se a chave tem um medidor.
Uma chave com medidor cobra de você a cada requisição. A do Google, a da OpenAI,
a da Anthropic e uma chave da AWS com as permissões erradas atrás dela são todas
medidores: o tráfego de outra pessoa cai na sua fatura, num ritmo que essa
pessoa escolhe e que você não vê. Uma chave sem medidor lê ou escreve os seus
dados. Uma chave service_role do Supabase é o exemplo mais claro, e nada nela
custa dinheiro por hora.
Essa única pergunta define a ordem.
Se a chave tem medidor, pare agora. A funcionalidade que a usava fica quebrada até você publicar a substituta, e essa é a troca certa, porque a fatura é a única parte disto que continua crescendo enquanto você planeja.
Se a chave lê dados e foi publicada no seu bundle, a leitura já aconteceu. Todo visitante que carregou a página tem uma cópia, e todo robô que foi atrás exatamente dessa sequência de caracteres também. Nada piora enquanto você descobre a ordem certa, e o que vale acertar é não se trancar para fora do seu próprio aplicativo no caminho.
O que cada tipo de chave realmente consegue fazer
| Chave | Gasta dinheiro | Lê seus dados | Dá para restringir no lugar? | Substituir quebra o aplicativo? |
|---|---|---|---|---|
Supabase sb_publishable_ ou anon | Não | Só as linhas que suas regras permitem | O lugar dela é o navegador | Não se aplica |
Stripe pk_live_ | Não | Não | O lugar dela é o navegador | Não se aplica |
Google AIza… | Sim, na sua fatura do Cloud | Não | Sim, e o Google diz para tentar isso primeiro | Restringir não. Rotacionar pode. |
| Chave da OpenAI ou da Anthropic | Sim, no ritmo de um estranho | Os arquivos e assistentes do projeto | Não existe variante publicável | Sim, até um servidor seu guardá-la |
Stripe sk_live_ ou rk_live_ | Sim | Sim, clientes e registros de pagamento | Uma chave restrita é a versão estreita | Não, há uma janela de sete dias |
AWS AKIA… mais a metade secreta | Sim, inclusive computação por hora | Seus buckets S3 | Deactivate, e dá para desfazer | Não, você pode ter duas chaves ao mesmo tempo |
Supabase sb_secret_ ou service_role | Não | Cada linha de cada tabela | Não | Sim, num projeto antigo |
Duas observações sobre essa tabela, porque as duas mudam o que vem depois.
Uma chave de acesso da AWS são duas sequências de caracteres, um identificador
que começa com AKIA e uma metade secreta, e a AWS exige as duas juntas para
assinar uma requisição. Ou seja, uma sequência AKIA sozinha num bundle não dá
para usar, e a razão para tratá-la como urgente mesmo assim é que as duas
metades quase sempre são coladas juntas. Abra o arquivo e procure a segunda
antes de decidir em qual caso você está.
O detalhe de cada provedor fica com o provedor: uma chave da OpenAI, uma chave de API do Google, uma chave secreta da Stripe e uma chave service_role do Supabase, que é a única com um procedimento próprio.
Parar a chave e substituir a chave são dois botões diferentes
Todo provedor daquela tabela dá os dois, e o pânico vai no segundo.
- Google. Restringir uma chave não muda a sequência de caracteres, então seu aplicativo continua funcionando. A orientação de segurança deles coloca isso antes de tudo: "First try to restrict your API keys", e rotacionar é a terceira opção, para quando uma restrição não é possível.
- Stripe. Expire key para uma chave sozinha, sem substituta envolvida. A posição deles sobre quando usar isso não tem meio-termo: "If a restricted or secret API key is exposed or compromised, rotate it immediately even if you aren't sure anyone saw it." Eles também separam as duas palavras que todo mundo usa como sinônimo. Exposure é a chave ter ficado visível onde não devia. Compromise é a prova de que alguém a usou.
- AWS. Deactivate é o controle, e o útil é que dá para desfazer. A AWS diz para não apagar a chave antiga enquanto você ainda estiver conferindo: "we recommend that you do not immediately delete the first access key. Instead, choose Actions and then choose Deactivate." Se algo que você esqueceu ainda precisar dela, é só ligar de novo.
- Supabase, num projeto antigo. Há uma única chave de desligar, e ela cobre
as duas chaves legadas.
anoneservice_rolesão assinadas pelo mesmo segredo, então desativar a que você quer matar para também a que o seu frontend usa. Os quatro passos que evitam isso valem mais lidos antes de mexer no interruptor do que depois.
Ler o log do período em que a chave esteve fora
A janela abre com o deploy que publicou a chave e fecha quando você a parou. É esse intervalo de datas que você usa como filtro no log do provedor.
Todo provedor da tabela guarda um. A Stripe mostra os logs de requisição de uma chave só, pelo menu de três pontos ao lado dessa chave na página de API Keys. A AWS coloca uma data de último uso em cada chave de acesso no console do IAM sem configurar nada, e registra as chamadas em si no CloudTrail. O Google traça o uso por chave no Cloud Console, que é também a leitura que a orientação deles pede antes de você mudar qualquer coisa numa chave. O Supabase guarda logs de API e de banco de dados do projeto, e como estreitar a janela numa chave do Supabase diz o que procurar neles.
O que você procura é tráfego que não consegue explicar: requisições a tabelas ou endpoints que o seu aplicativo nunca toca, volume em horários em que ninguém estava usando, exclusões que ninguém fez. Depois olhe os dados em si, porque uma mensagem de suporte sobre um registro que mudou sozinho é como a maioria desses casos é descoberta de fato, e ela chega semanas depois.
Muitas vezes não dá para ter certeza, e a Stripe diz o mesmo da detecção dela: "Stripe doesn't guarantee detection of all exposed or compromised keys." Então "nada no log" é um resultado, e vale escrever isso com o intervalo de datas ao lado.
Rotacionar sem derrubar o aplicativo
Três dos quatro provedores acima dão uma janela em que a chave antiga e a nova funcionam juntas, e é isso que evita uma queda.
Stripe. "When you rotate a key in the Dashboard, both the old and new keys work for up to 7 days." A caixa de diálogo também tem a opção Now, e a documentação deles é explícita: se você escolher, a chave antiga é apagada. Esse é o botão da emergência com medidor, e não o de uma migração tranquila. O conselho deles sobre quando soltar a antiga é uma medição, não uma data: confira os logs de requisição dela e só a deixe expirar depois que o volume estiver em zero por algumas horas ou dias.
Google. Rotacionar cria a chave nova carregando todas as restrições da antiga e, nas palavras deles, "both the old and new key are accepted" durante a janela em que você move seus aplicativos. Se apagar a antiga cedo demais e algo quebrar, há um caminho de volta: uma chave de API do Google apagada pode ser restaurada em até 30 dias.
AWS. A sequência está na documentação deles e começa pela chave nova: crie a segunda chave de acesso enquanto a primeira ainda está ativa, mova cada aplicação para ela, confira a data de último uso da antiga, desative, e só então apague. Um teto para contar: um usuário do IAM pode ter no máximo duas chaves de acesso, então uma terceira aplicação ainda numa chave antiga não tem para onde ir.
Supabase, num projeto antigo. Sem janela. A rotação direta das chaves
legadas anon e service_role não é mais suportada, então invalidar uma delas
é uma migração para o novo par de chaves, e a ordem dos quatro passos é o que
mantém o aplicativo no ar.
Ela continua no seu histórico do Git
Continua, e a documentação do GitHub sobre isso começa mandando você para outro lugar.
A página deles sobre remover dados sensíveis devolve você à chave: "if the sensitive data you need to remove is a secret (e.g. password/token/credential), as is often the case, then as a first step you need to revoke and/or rotate that secret", e depois: "Once the secret is revoked or rotated, it can no longer be used for access, and that may be sufficient to solve your problem. Going through the extra steps to rewrite the history and remove the secret may not be warranted."
A razão que eles dão é o que um force push não alcança. Depois de reescrever o seu histórico, os commits antigos continuam lá "In any clones or forks of your repository" e "Directly via their SHA-1 hashes in cached views on GitHub". O suporte consegue limpar as visualizações em cache e as referências em pull requests se você pedir, e traça o próprio limite: ele "will only assist in the removal of sensitive data in cases where we determine that the risk can't be mitigated by rotating affected credentials." Um fork fica com a cópia dele de qualquer jeito, e o GitHub não pode dar o contato do dono.
Então a ordem que eles descrevem é a prática: revogar a chave e depois decidir se reescrever o histórico compensa os efeitos colaterais. Revogar alcança cópias que uma reescrita não alcança, inclusive a de um clone que você nem sabe que existe e a de um print que alguém guardou.
Como impedir que a próxima entre no bundle
Uma chave entra no seu bundle por um deploy, e os deploys continuam acontecendo. Cada um deles é mais uma chance de um valor que você colocou num painel de segredos acabar num arquivo que o navegador baixa. É por isso que uma verificação feita no mês passado descreve o aplicativo do mês passado.
A nossa varredura gratuita responde à pergunta que trouxe você até aqui.
- as nove verificações contra a sua URL no ar, em uns 20 segundos
- cada chave que ela enxerga de fora, classificada e não apenas encontrada
- uma nota e os achados, sem conta
O Reeve Monitor roda essas verificações de novo sem você pedir.
- as nove verificações a cada hora, em até três aplicativos
- uma mensagem quando um resultado muda, para que a chave que saiu no deploy de ontem à noite não fique esperando você olhar
- se o aplicativo responde, a cada 60 segundos
- um relatório mensal do que ele viu
O Reeve Care guarda uma cópia do seu banco de dados Supabase, para as chaves que conseguem escrever.
- uma cópia criptografada toda noite, guardada onde o seu projeto não alcança
- cada cópia verificada antes de contar, contando as linhas de cada tabela
- uma restauração em um clique quando você precisar
- os seus arquivos enviados também, assim que você conectar uma credencial de Storage
- tudo o que o Monitor faz
Uma chave vazada que só consegue ler deixa os seus dados onde estavam. Uma chave
service_role, ou uma da AWS com permissão de escrita, consegue esvaziar uma
tabela, e nenhuma rotação depois traz as linhas de volta.
O dia em que um agente de IA apagou um banco de dados de produção
mostra como isso é por dentro.
O que fazer agora
O que fazer
- Identifique a chave antes de mexer nela. Uma chave
anonousb_publishable_do Supabase e umapk_live_da Stripe estão no lugar certo, e dos 1.332 aplicativos em que encontramos uma chave que merecia aviso, 1.142 tinham uma chave de API do Google, que pede uma restrição em vez de uma rotação. - Pergunte se ela tem um medidor. Se as requisições de outra pessoa caem na sua fatura, pare a chave agora e deixe a funcionalidade quebrar.
- Use tanto o controle que para quanto o que substitui: Expire key na Stripe, Deactivate na AWS, uma restrição por site e por API no Google.
- Substitua depois, dentro da janela do provedor. A Stripe dá sete dias com as duas chaves ativas, o Google aceita as duas enquanto você migra, e a AWS deixa você ter duas chaves de acesso ao mesmo tempo.
- Leia o log do provedor no período entre o deploy e a parada, e anote o que encontrou, inclusive "nada".
- Deixe o histórico do Git por último. Depois que a chave está revogada, a própria orientação do GitHub diz que reescrever o histórico pode não valer a pena.
Se você preferir percorrer tudo isso como uma lista, o checklist de segurança em 10 minutos cobre este assunto e as outras coisas que vale desligar num aplicativo recém-lançado.
Perguntas frequentes
Minha chave de API vazou. O que faço primeiro?
Descubra se a chave pode gastar dinheiro. Uma chave que cobra por requisição está custando alguma coisa agora mesmo, então pare a chave imediatamente e aceite que a funcionalidade que a usa fique quebrada por alguns minutos. Uma chave que só lê dados já foi lida se estava pública, então use o tempo para fazer a troca numa ordem que não tranque você para fora do seu próprio aplicativo.
Revogar ou rotacionar primeiro?
Revogar primeiro, se a chave tiver um medidor. Parar a chave antiga e emitir uma nova são dois controles separados em todo provedor, e só o primeiro fecha o buraco. A exceção é uma chave que você pode restringir no lugar disso: o Google manda tentar uma restrição por site e por API antes mesmo de rotacionar uma chave de API do Google.
Como sei se alguém usou a minha chave?
Reduza a janela ao período entre o deploy que publicou a chave e o momento em que você a parou, e leia o log do provedor nesse intervalo. A Stripe mostra logs de requisição por chave, a AWS mostra uma data de último uso na chave de acesso e registra as chamadas no CloudTrail, o Google traça o uso por chave, e o Supabase guarda logs de API e de banco de dados. Você procura requisições a coisas que o seu aplicativo nunca toca e tráfego em horários em que ninguém estava usando. Muitas vezes não dá para ter certeza, e esse é um resultado normal.
Rotacionar a chave vai quebrar meu aplicativo?
Normalmente não, se você usar a janela que o provedor dá. A Stripe mantém a chave antiga e a nova funcionando juntas por até sete dias. O Google emite a chave nova com as restrições da antiga e aceita as duas enquanto você migra. A AWS deixa você ter duas chaves de acesso ao mesmo tempo, então a nova já está ativa antes de a antiga sair. A exceção é um projeto antigo do Supabase, onde a chave que desativa as chaves legadas leva junto a chave do seu frontend.
A chave está no meu histórico do Git. Apagar o arquivo basta?
Não, e reescrever o histórico provavelmente também não é a resposta. A documentação do GitHub diz para revogar ou rotacionar o segredo primeiro, e que depois disso entrar na reescrita do histórico pode não valer a pena. Um force push não alcança as cópias em forks e clones, nem as visualizações em cache acessíveis pelo hash do commit. Deixar a chave inútil cobre todas as cópias de uma vez.