Chave do Supabase exposta: qual pode ir para o navegador?
A chave publicável do Supabase (sb_publishable_..., ou a antiga anon) pode ficar no navegador, desde que todas as tabelas tenham RLS ligado. A chave secreta (sb_secret_..., ou a antiga service_role) nunca pode: ela ignora o RLS e abre o banco inteiro. Se a secreta vazou, troque a chave antes de qualquer outra coisa.
Recomendação da Heartscale: procure a chave secreta no código, no build publicado e no histórico do git antes de concluir que ela não vazou. Se ela aparecer em qualquer um e o app já tem usuários reais, troque a chave na hora e peça uma revisão das tabelas e dos logs.
As regras abaixo seguem a documentação oficial do Supabase sobre API keys, conferida em outubro de 2026. Tudo aqui vale para o seu próprio projeto.
As quatro chaves do Supabase
O Supabase tem dois sistemas de chave convivendo: o novo e o legado. Cada um tem uma chave pública e uma secreta.
| Chave | Formato | Pode ir para o navegador? | Respeita o RLS? |
|---|---|---|---|
| Publicável | sb_publishable_... |
Sim | Sim |
anon (legada) |
JWT longo, começa com eyJ |
Sim | Sim |
| Secreta | sb_secret_... |
Nunca | Não, ignora |
service_role (legada) |
JWT longo, começa com eyJ |
Nunca | Não, ignora |
A documentação diz que a chave secreta não pode estar no navegador, em app distribuído, em documento público nem no controle de versão, nem mesmo em localhost. Ela também informa que o Supabase planeja descontinuar as chaves anon e service_role até o fim de 2026.
Por que a chave publicável pode ficar exposta
Apps feitos no Lovable, no Bolt ou no Base44 costumam falar com o banco direto do navegador. Para isso, a chave publicável vai junto com o site, e qualquer visitante consegue vê-la.
Isso é esperado. Sem usuário logado, a chave publicável entra no banco com o papel anon. Com usuário logado, com o papel authenticated. As políticas de RLS decidem o que cada papel pode ler ou alterar. Por isso o Supabase pede que você ligue o RLS em todas as tabelas antes de publicar.
O risco da chave publicável está nas tabelas sem RLS ou com política aberta demais. Trocar a chave não resolve isso. O passo a passo para ligar e testar está em RLS no Supabase: como configurar e testar o seu app.
A chave secreta usa o papel service_role, que tem o atributo BYPASSRLS. As políticas nunca se aplicam a ela. Quem tem a chave secreta tem o banco.
Passo 1: veja qual chave o seu código usa
Abra o repositório do seu app e procure as chaves e os nomes de variável mais comuns:
grep -rn --exclude-dir=node_modules -E "sb_secret_|service_role|SERVICE_ROLE_KEY|SUPABASE_SECRET" .
grep -rn --exclude-dir=node_modules -E "sb_publishable_|anon|PUBLISHABLE_KEY" .
Para cada resultado, pergunte onde o código roda:
- Navegador: arquivos em
src/, componentes React e qualquer variável com prefixoVITE_. O Vite avisa que variáveisVITE_entram no código do cliente e não devem guardar chaves de API. Aqui só pode aparecer a publicável. - Servidor: Edge Functions em
supabase/functions/, rotas de servidor e scripts que rodam fora do navegador. Aqui a secreta pode ser usada, lida de uma variável de ambiente e nunca escrita no arquivo.
Se o resultado é um JWT longo que começa com eyJ, compare com as chaves em Settings > API Keys no painel do Supabase. Assim você sabe se é a anon ou a service_role.
Confira também o que de fato sai para o navegador. Rode o build do seu app e procure no resultado:
npm run build
grep -rn -E "sb_secret_|service_role" dist/
Qualquer resultado aqui é uma chave secreta publicada no site.
Um sinal extra: o Supabase confere o User-Agent e devolve 401 Unauthorized quando uma chave secreta é usada a partir de um navegador. Se o seu app recebe 401 em chamadas que deveriam funcionar, verifique se a chave secreta foi parar no front. A documentação deixa claro que esse bloqueio é só uma salvaguarda: a chave vazada continua utilizável por outras ferramentas.
Passo 2: procure no histórico do git
Apagar a chave do arquivo não tira a chave do histórico. Busque em todos os branches:
git log --all --oneline -S "sb_secret_"
git log --all --oneline -G "service_role|sb_secret_|SERVICE_ROLE_KEY"
O -S mostra os commits que mudam o número de vezes que o texto aparece. O -G mostra os commits cujo diff adiciona ou remove uma linha que casa com a expressão (git log). Para ver o trecho de um commit encontrado, use git show <hash>.
Se a chave aparece em qualquer commit de um repositório que já foi público, ou compartilhado com alguém, trate como vazada.
Passo 3: a chave secreta vazou, faça nesta ordem
A ordem vem da documentação do Supabase. Trocar a chave vem antes de limpar o código, porque a chave antiga funciona até ser apagada.
- Corrija a causa. Tire a chave do front, das variáveis
VITE_e de qualquer arquivo versionado. Mova a operação que precisava dela para uma Edge Function ou outro código de servidor. - Crie uma chave secreta nova em Settings > API Keys. A documentação recomenda uma chave secreta por componente do backend, para que um vazamento futuro exija trocar só uma.
- Substitua a chave antiga em cada lugar que a usa: Edge Functions, servidor, tarefas agendadas e integrações.
- Confirme que todos os componentes usam a chave nova.
- Apague a chave antiga. Para chave secreta, apagar não tem volta, por isso só depois do passo 4. Para a
service_rolelegada, a opção é desativar, na mesma seção, e isso pode ser desfeito.
Atenção a dois pontos das chaves legadas:
- Criar chaves novas não desativa a
anonnem aservice_role. Os dois sistemas funcionam ao mesmo tempo até você desativar as legadas. - Nas Edge Functions, as variáveis
SUPABASE_ANON_KEYeSUPABASE_SERVICE_ROLE_KEYtrazem as chaves legadas. A documentação orienta usarSUPABASE_SECRET_KEYSou o SDK@supabase/server. Se o app ainda usa aanonno navegador, troque-a pela publicável antes de desativar as legadas, para o app não parar.
Passo 4: limpe o histórico, se fizer sentido
O GitHub também põe a troca da credencial como primeiro passo. Reescrever o histórico vem depois e tem custo:
- os hashes dos commits mudam e quem tem um clone antigo pode reenviar a chave;
- forks e pull requests antigos continuam com o conteúdo;
- a proteção de branch contra force push precisa ser desligada por um tempo.
Se a chave já foi trocada e apagada, o valor no histórico não abre mais nada. Muitas equipes param aqui. Se você decidir limpar, o GitHub documenta o uso do git-filter-repo com a opção --sensitive-data-removal.
Passo 5: revise o RLS e o Security Advisor
A chave secreta ignora o RLS, então políticas boas não protegeram nada enquanto ela esteve exposta. Mas a troca é um bom momento para revisar a chave publicável, que continua no site:
- confira se todas as tabelas expostas têm RLS ligado e políticas por operação;
- lembre que as permissões da tabela (
grant) são checadas antes do RLS; - abra o Security Advisor do projeto e resolva os alertas que ele listar.
Passo 6: olhe os logs do período
No painel do Supabase, abra Logs e escolha o tipo API Gateway (Logs). Ajuste o Time Range para o período em que a chave esteve exposta e filtre por Method e Pathname. Procure gravações e exclusões que o seu app não faz, picos de volume e tabelas que ninguém costuma consultar. Use Download logs para guardar o que encontrar.
A retenção dos logs depende do plano. Faça isso logo, antes de os registros mais antigos sumirem.
Se os logs mostram acesso indevido a dados de pessoas, isso pode ser um incidente de segurança pela LGPD, com prazo para comunicar a ANPD. Veja LGPD para app feito com IA: o mínimo antes de lançar.
E se só a chave publicável vazou?
Ela foi feita para ser pública, então o vazamento em si não exige troca. O que exige atenção é o que ela alcança. Teste as tabelas com a chave publicável e sem usuário logado: tudo o que voltar é o que qualquer visitante vê. Se voltar dado que não deveria, corrija o RLS ou as permissões da tabela.
Na nossa revisão, fazemos essa consulta em cada tabela exposta, primeiro com a chave publicável e sem login, depois como um usuário comum tentando ler dados de outro.
Para uma revisão mais ampla do app, veja a página de segurança de app feito com IA.
Fazer sozinho ou contratar?
Se a busca não achou a chave secreta, este guia basta. A tabela mostra quando a troca pede mais cuidado.
| Situação | Caminho |
|---|---|
| Só a chave publicável aparece no site e todas as tabelas têm RLS ligado | Siga o passo 5 e teste as tabelas sem login |
| A chave secreta vazou e o app já tem usuários reais | Troque a chave agora, na ordem do passo 3, e peça uma revisão das tabelas e dos logs |
| A chave secreta é usada por Edge Functions, tarefas agendadas e integrações que você não sabe listar | Chame uma equipe para mapear cada uso antes de apagar a chave antiga |
| O app tem usuários reais e os logs mostram acessos que o app não faz | Peça o diagnóstico da Heartscale e confira o prazo de comunicação da LGPD |
Peça o diagnóstico da Heartscale
Nossa equipe procura chaves no código, no build publicado e no histórico do git, confere onde cada uma roda e testa o RLS das tabelas com a chave publicável. Se houver chave secreta exposta, você recebe a ordem de troca para o seu caso e a correção do código que dependia dela. A revisão reduz o risco, mas não o zera.
O diagnóstico de lançamento custa R$ 99 (de R$ 398) e é abatido se você fechar a correção. Só trabalhamos em app de quem prova ser dono.