FixVibe

// docs / baas security / supabase service role exposure

Chave de role de serviço Supabase exposta em JavaScript: o que significa e como encontrá-la

A chave de role de serviço do Supabase é a chave mestra do seu banco de dados. Quem a possuir contorna a segurança em nível de linha, pode ler cada coluna de cada tabela e pode escrever ou apagar o que quiser. Ela é projetada para viver exclusivamente em código do lado do servidor — nunca no navegador. Quando uma ferramenta de codificação com IA a publica no bundle JavaScript, seu banco de dados está, na prática, público. Este artigo explica o formato do JWT que identifica uma chave vazada, os três padrões de ferramentas de IA que produzem o vazamento, o que fazer na primeira hora após a detecção e como varrer automaticamente antes dos usuários.

O que é a chave de role de serviço

O Supabase emite duas chaves distintas para cada projeto: a chave anon (também chamada de chave publicável em projetos mais novos) e a chave service_role. Ambas são JSON Web Tokens assinados pelo segredo JWT do seu projeto. A diferença é o claim role embutido no payload do JWT — anon para a chave pública, service_role para a chave mestra. PostgREST, Supabase Storage e Supabase Auth todos entram em modo de contornar-tudo quando veem o claim service_role.

Decodifique qualquer chave Supabase em jwt.io e olhe o payload. O formato de um JWT de role de serviço é inconfundível:

Payload decodificado de um JWT de role de serviço (mostrado como bloco destacado abaixo).

json
{
  "iss": "supabase",
  "ref": "[project-ref]",
  "role": "service_role",
  "iat": 1700000000,
  "exp": 2000000000
}

Projetos Supabase mais novos emitem chaves tipo segredo com o prefixo sb_secret_ em vez de JWT. O comportamento é idêntico — qualquer coisa carregando sb_secret_ em um bundle público é igualmente catastrófica.

Como ferramentas de codificação com IA vazam a chave de role de serviço

Vimos os mesmos três padrões em milhares de apps gerados por IA. Cada um começa com um desenvolvedor pedindo ajuda a uma ferramenta de IA e termina com a chave de serviço inline em um bundle.

Padrão 1: arquivo .env único com prefixo NEXT_PUBLIC_

O desenvolvedor pede à ferramenta de IA para "configurar o Supabase" e aceita um único .env com ambas as chaves. A ferramenta de IA — treinada em um corpus onde a maioria das variáveis de ambiente é exposta via NEXT_PUBLIC_* — prefixa ambas com NEXT_PUBLIC_. O Next.js incorpora qualquer coisa que corresponda a esse prefixo no bundle do cliente em tempo de build. Publique no Vercel, e a chave de serviço está em main.[hash].js.

Padrão 2: chave errada na chamada createClient

O desenvolvedor cola ambas as chaves em um arquivo config.ts que a IA gerou, e a IA popula erroneamente a chamada createClient() do lado do navegador com process.env.SUPABASE_SERVICE_ROLE_KEY. O build puxa a variável e o JWT vai parar no bundle.

Padrão 3: chave de role de serviço hardcoded em scripts de seed

O desenvolvedor pede à ferramenta de IA para escrever um script que popula o banco. A IA escreve a chave de role de serviço diretamente no arquivo (em vez de ler do ambiente), commita o arquivo no repositório, e o repositório público do GitHub ou a rota /scripts/seed.js do app publicado agora está servindo a chave.

Como o bundle scan do FixVibe detecta o vazamento

A verificação de segredos em bundle do FixVibe baixa cada arquivo JavaScript referenciado pelo app publicado — entry chunks, chunks lazy-loaded, web workers, service workers — e os passa por um detector que decodifica qualquer coisa correspondente ao formato JWT (eyJ[base64-header].eyJ[base64-payload].[signature]). Se o payload decodificado contém "role": "service_role", a varredura reporta como achado crítico com o caminho do arquivo e a linha exata onde a chave aparece. A mesma verificação também detecta o padrão mais novo sb_secret_* por prefixo.

A varredura nunca se autentica com a chave descoberta. Identifica o formato e reporta o vazamento — usar a chave para provar a explorabilidade seria acesso não autorizado ao seu banco de dados. A prova está no próprio payload do JWT.

Detectada — o que fazer na primeira hora

Uma chave de role de serviço vazada é uma emergência em produção. Assuma que a chave foi coletada — atacantes monitoram bundles públicos em tempo real. Trate o banco como comprometido até que tenha rotacionado a chave e auditado a atividade recente.

  1. Rotacione a chave imediatamente. No painel do Supabase, vá para Configurações do Projeto → API → Service role key → Reset. A chave antiga é invalidada em segundos. Qualquer código do lado do servidor que use a chave deve ser atualizado e republicado antes da rotação se concretizar.
  2. Audite a atividade recente do banco. Abra Database → Logs no painel. Filtre os últimos 7 dias. Procure consultas SELECT * incomuns contra tabelas com PII, declarações UPDATE ou DELETE grandes e requisições de IPs fora da sua infraestrutura conhecida. O Supabase registra o cabeçalho x-real-ip em cada requisição.
  3. Verifique objetos de storage. Visite Storage → Logs e revise downloads recentes de arquivos. Uma chave de role de serviço vazada também dá acesso de contornar-tudo a buckets privados.
  4. Remova a chave do controle de versão. Mesmo após a rotação, deixar o JWT no seu histórico git significa que ele é descobrível no repositório público. Use git filter-repo ou BFG Repo-Cleaner para limpá-lo do histórico, depois force-push (avise os colaboradores primeiro).
  5. Varra de novo após a correção. Rode uma nova varredura do FixVibe contra o app republicado. O achado de segredos em bundle deve desaparecer. Confirme que nenhum JWT service_role e nenhuma string sb_secret_* permanece em nenhum chunk.

Prevenindo o vazamento desde o início

A correção estrutural é disciplina de nomenclatura mais guardrails em nível de ferramenta:

  • Nunca prefixe a chave de serviço com NEXT_PUBLIC_*, VITE_* ou qualquer outro prefixo de incorporação em bundle. A convenção de nomenclatura é a fronteira — todo framework a respeita.
  • Mantenha a chave de serviço fora do .env da máquina do desenvolvedor por completo. Leia-a de um gerenciador de segredos (Doppler, Infisical, variáveis de ambiente criptografadas do Vercel) no deploy, nunca a commite localmente.
  • <strong>Mark every Supabase client construction with explicit context.</strong> Files named <code>supabase/browser.ts</code> use the anon key; files named <code>supabase/server.ts</code> use the service-role key with <code>import 'server-only'</code> at the top. The <code>server-only</code> import causes a build error if a client component tries to consume the module.
  • <strong>Add a pre-commit hook that greps for JWT-shaped strings.</strong> <code>git diff --staged | grep -E 'eyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+'</code> catches both anon and service tokens before they leave your machine.
  • Adicione um gate de CI que varra a saída do build. Após next build, faça grep da saída de .next/static/chunks/ pela string service_role. Faça o build falhar se algo corresponder.
bash
# Pre-commit hook: refuse any staged JWT-shaped string.
git diff --staged \
  | grep -E 'eyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+' \
  && echo "JWT detected in staged changes — refusing commit" \
  && exit 1

# CI gate: fail the build if "service_role" shipped to the static bundle.
grep -RE 'service_role|sb_secret_' .next/static/chunks/ \
  && echo "Service-role credential leaked into bundle" \
  && exit 1

Perguntas frequentes

Quão rápido atacantes realmente encontram chaves de role de serviço Supabase vazadas?

Scanners de bundles públicos vasculham novos deploys em minutos. Pesquisadores documentaram exploits funcionais contra novos projetos Supabase em menos de uma hora desde o primeiro deploy. Trate qualquer exposição de role de serviço como uma janela de 60 minutos, não 60 dias.

Rotacionar a chave é o suficiente, ou tenho que assumir exfiltração de dados?

A rotação invalida a chave vazada mas não desfaz dados já puxados. Se suas tabelas contêm PII, dados de pagamento ou qualquer dado regulado, pode haver obrigação de notificação sob LGPD/GDPR (72 horas), CCPA ou HIPAA. Audite os logs e consulte assessoria jurídica se a auditoria mostrar acessos suspeitos.

RLS pode me proteger se a chave de role de serviço vazar?

Não. A segurança em nível de linha é completamente contornada pelo claim service_role. É por design — a chave existe precisamente para permitir que código backend pule o RLS para operações de admin. A mitigação é garantir que a chave nunca chegue a um contexto onde um atacante possa lê-la.

Isso se aplica ao novo modelo de chave publicável / secret do Supabase (<code>sb_publishable_</code> / <code>sb_secret_</code>)?

Sim — classe de risco idêntica. A chave sb_secret_* é o novo formato de chave secreta que substitui o JWT de role de serviço para projetos mais novos. Qualquer coisa carregando sb_secret_* em um bundle é tão catastrófica quanto um JWT de role de serviço vazado. O detector de segredos em bundle do FixVibe detecta ambos os formatos.

E sobre a chave anon / publicável — ela é segura no bundle?

Sim, por design. A chave anon é feita para viver no navegador e é o que cada cliente web Supabase usa. Sua segurança depende inteiramente do RLS estar corretamente configurado em cada tabela pública. Veja o artigo Scanner de RLS Supabase para o que verificar.

Próximos passos

Rode uma varredura do FixVibe contra sua URL de produção — a verificação de segredos em bundle é gratuita, sem cadastro, e reporta exposição de service_role em menos de um minuto. Combine com o artigo Scanner de RLS Supabase para verificar que a camada RLS está fazendo seu trabalho, e o Checklist de segurança de buckets Supabase Storage para bloquear o acesso a arquivos. Para o contexto sobre por que ferramentas de IA geram essa classe de vazamento com tanta confiabilidade, leia Por que ferramentas de codificação com IA deixam lacunas de segurança.

// varra sua superfície baas

Encontre a tabela aberta antes que outra pessoa o faça.

Coloque uma URL de produção. O FixVibe enumera os provedores BaaS com que seu app conversa, identifica seus endpoints públicos e relata o que um cliente não autenticado pode ler ou escrever. Grátis, sem instalação, sem cartão.

  • Plano gratuito — 3 varreduras/mês, sem cartão de cadastro.
  • Identificação BaaS passiva — sem necessidade de verificação de domínio.
  • Supabase, Firebase, Clerk, Auth0, Appwrite e mais.
  • Prompts de correção com IA em cada achado — cole de volta no Cursor / Claude Code.
Rodar varredura BaaS gratuita

sem necessidade de cadastro

Chave de role de serviço Supabase exposta em JavaScript: o que significa e como encontrá-la — Docs · FixVibe