// 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 da sua base de dados. Quem a possuir contorna a segurança ao nível da linha, pode ler cada coluna de cada tabela e pode escrever ou apagar o que quiser. Está concebida 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, a sua base de dados está, na prática, pública. 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 deteção e como varrer automaticamente antes dos utilizadores.
O que é a chave de role de serviço
O Supabase emite duas chaves distintas para cada projeto: a chave anon (também chamada chave publicável em projetos mais recentes) e a chave service_role. Ambas são JSON Web Tokens assinados pelo segredo JWT do seu projeto. A diferença é o claim role embebido 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.
Descodifique qualquer chave Supabase em jwt.io e olhe para o payload. O formato de um JWT de role de serviço é inconfundível:
Payload descodificado de um JWT de role de serviço (mostrado como bloco destacado abaixo).
{
"iss": "supabase",
"ref": "[project-ref]",
"role": "service_role",
"iat": 1700000000,
"exp": 2000000000
}Projetos Supabase mais recentes emitem chaves do tipo segredo com o prefixo sb_secret_ em vez de JWT. O comportamento é idêntico — qualquer coisa que carregue sb_secret_ num 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 programador a pedir ajuda a uma ferramenta de IA e termina com a chave de serviço inline num bundle.
Padrão 1: ficheiro .env único com prefixo NEXT_PUBLIC_
O programador pede à ferramenta de IA para "configurar o Supabase" e aceita um único .env com ambas as chaves. A ferramenta de IA — treinada num corpus onde a maioria das variáveis de ambiente são expostas 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 programador cola ambas as chaves num ficheiro config.ts que a IA gerou, e a IA preenche erradamente 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 ao bundle.
Padrão 3: chave de role de serviço hardcoded em scripts de seed
O programador pede à ferramenta de IA para escrever um script que popula a base de dados. A IA escreve a chave de role de serviço diretamente no ficheiro (em vez de ler do ambiente), faz commit do ficheiro no repositório, e o repositório público do GitHub ou a rota /scripts/seed.js da app publicada está agora a servir a chave.
Como o bundle scan do FixVibe deteta o vazamento
A verificação de segredos em bundle do FixVibe descarrega cada ficheiro JavaScript referenciado pela app publicada — entry chunks, chunks lazy-loaded, web workers, service workers — e passa-os por um detetor que descodifica qualquer coisa correspondente ao formato JWT (eyJ[base64-header].eyJ[base64-payload].[signature]). Se o payload descodificado contém "role": "service_role", o varrimento reporta como achado crítico com o caminho do ficheiro e a linha exata onde a chave aparece. A mesma verificação também deteta o padrão mais recente sb_secret_* por prefixo.
O varrimento 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 à sua base de dados. A prova está no próprio payload do JWT.
Detetada — 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 recolhida — atacantes monitorizam bundles públicos em tempo real. Trate a base de dados como comprometida até ter rotacionado a chave e auditado a atividade recente.
- 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 de a rotação se concretizar.
- Audite a atividade recente da base de dados. Abra Database → Logs no painel. Filtre os últimos 7 dias. Procure consultas
SELECT *invulgares contra tabelas com PII, declaraçõesUPDATEouDELETEgrandes e pedidos de IPs fora da sua infraestrutura conhecida. O Supabase regista o cabeçalhox-real-ipem cada pedido. - Verifique objetos de storage. Visite Storage → Logs e reveja descargas recentes de ficheiros. Uma chave de role de serviço vazada também dá acesso de contornar-tudo a buckets privados.
- Remova a chave do controlo de versões. Mesmo após a rotação, deixar o JWT no seu histórico git significa que é descobrível no repositório público. Use
git filter-repoou BFG Repo-Cleaner para o limpar do histórico, depois force-push (avise primeiro os colaboradores). - Varra de novo após a correção. Execute um novo varrimento do FixVibe contra a app republicada. O achado de segredos em bundle deve desaparecer. Confirme que nenhum JWT
service_rolee nenhuma stringsb_secret_*permanece em nenhum chunk.
Prevenir 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 — todos os frameworks a respeitam. - Mantenha a chave de serviço fora do
.envda máquina do programador por completo. Leia-a de um gestor de segredos (Doppler, Infisical, variáveis de ambiente cifradas do Vercel) no deploy, nunca faça commit 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 stringservice_role. Faça o build falhar se algo corresponder.
# 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 1Perguntas frequentes
Quão rapidamente os atacantes realmente encontram chaves de role de serviço Supabase vazadas?
Scanners de bundles públicos vasculham novos deploys em minutos. Investigadores 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 é suficiente, ou tenho de assumir exfiltração de dados?
A rotação invalida a chave vazada mas não desfaz dados já extraídos. Se as suas tabelas contêm PII, dados de pagamento ou qualquer dado regulado, pode haver obrigação de notificação ao abrigo do RGPD (72 horas), CCPA ou HIPAA. Audite os logs e consulte assessoria jurídica se a auditoria mostrar acessos suspeitos.
RLS pode proteger-me se a chave de role de serviço vazar?
Não. A segurança ao nível da linha é completamente contornada pelo claim service_role. É by design — a chave existe precisamente para permitir que código backend salte o RLS para operações de admin. A mitigação é garantir que a chave nunca chega a um contexto onde um atacante a possa ler.
Isto aplica-se 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 recentes. Qualquer coisa carregando sb_secret_* num bundle é tão catastrófica quanto um JWT de role de serviço vazado. O detetor de segredos em bundle do FixVibe deteta ambos os formatos.
E sobre a chave anon / publicável — é segura no bundle?
Sim, by design. A chave anon é feita para viver no navegador e é o que cada cliente web Supabase usa. A 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
Execute um varrimento do FixVibe contra a sua URL de produção — a verificação de segredos em bundle é gratuita, sem registo, 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á a fazer o seu trabalho, e o Checklist de segurança de buckets Supabase Storage para bloquear o acesso a ficheiros. Para o contexto sobre porquê as ferramentas de IA geram esta classe de vazamento com tanta fiabilidade, leia Porque é que as ferramentas de codificação com IA deixam lacunas de segurança.
