Pular para o conteúdo
Heartscale

supabasePublicado em · Equipe Heartscale

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 prefixo VITE_. O Vite avisa que variáveis VITE_ 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.

  1. 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.
  2. 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.
  3. Substitua a chave antiga em cada lugar que a usa: Edge Functions, servidor, tarefas agendadas e integrações.
  4. Confirme que todos os componentes usam a chave nova.
  5. Apague a chave antiga. Para chave secreta, apagar não tem volta, por isso só depois do passo 4. Para a service_role legada, 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 anon nem a service_role. Os dois sistemas funcionam ao mesmo tempo até você desativar as legadas.
  • Nas Edge Functions, as variáveis SUPABASE_ANON_KEY e SUPABASE_SERVICE_ROLE_KEY trazem as chaves legadas. A documentação orienta usar SUPABASE_SECRET_KEYS ou o SDK @supabase/server. Se o app ainda usa a anon no 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.

Pedir o diagnóstico

Perguntas frequentes

Perguntas frequentes

A chave anon do Supabase aparecer no código do site é um problema?

Não, desde que todas as tabelas expostas tenham RLS ligado e políticas corretas. A chave anon, hoje chamada de publicável, foi feita para ficar no navegador. Quem limita o que ela acessa são as políticas do banco.

O que acontece se a service_role key do Supabase vazar?

Quem tiver a chave consegue ler, alterar e apagar qualquer dado do projeto, porque ela ignora o RLS. Corrija a causa do vazamento, crie uma chave nova, troque em todos os lugares, apague a antiga e revise os logs.

Apagar a chave do código e dar commit resolve o vazamento?

Não. A chave continua no histórico do git e em qualquer cópia ou fork do repositório. O primeiro passo é trocar a chave no Supabase. Limpar o histórico vem depois e não substitui a troca.

Como saber se a chave no meu código é a publicável ou a secreta?

Pelo prefixo. A publicável começa com sb_publishable_ e a secreta com sb_secret_. Chaves longas que começam com eyJ são as antigas, anon ou service_role; compare com as que aparecem em Settings > API Keys no painel.

Posso usar a chave secreta do Supabase numa variável VITE_?

Não. O Vite coloca toda variável com prefixo VITE_ no código que vai para o navegador. A chave secreta deve ficar só no servidor, por exemplo numa Edge Function.

Vale a pena contratar alguém para resolver uma chave do Supabase exposta?

Vale quando a chave secreta vazou num app com usuários reais, quando várias partes do backend a usam ou quando ninguém do time lê o código. A Heartscale faz um diagnóstico de lançamento por R$ 99 (de R$ 398), abatido se você contratar a correção em 15 dias. Troque a chave antes, sem esperar o diagnóstico.

Comece pelo diagnóstico.

O diagnóstico custa R$ 99, pagos por Pix. Entregamos o relatório em 2 dias úteis e apresentamos os resultados em reunião. O valor é abatido se você contratar o serviço em até 15 dias. Tudo o que configuramos fica no seu nome.

Reduz o risco, não o zera. Atendemos somente o dono do app, mediante comprovação.