FixVibe

// docs / baas security / supabase rls scanner

Scanner de RLS Supabase: encontre tabelas com segurança em nível de linha ausente ou quebrada

A segurança em nível de linha (RLS) é a única coisa entre os dados dos seus clientes e a internet quando você publica uma aplicação apoiada por Supabase. Ferramentas de codificação com IA geram código em formato de RLS que compila, é publicado e vaza dados silenciosamente — tabelas criadas sem RLS habilitado, políticas que leem mas nunca restringem, predicados que comparam uma coluna consigo mesma. Este artigo mostra o que um scanner de RLS Supabase pode provar de fora, as quatro formas de RLS quebrada que aparecem em apps gerados por IA e como varrer seu próprio deploy em menos de um minuto.

O que uma varredura RLS externa pode provar

Uma varredura RLS passiva roda contra o endpoint PostgREST que o Supabase expõe em https://[project].supabase.co/rest/v1/. Usa apenas a chave publicável anon — a mesma que seu navegador usa — e sonda metadados de listagem de tabelas, leituras anônimas e escritas anônimas. Nunca se autentica como usuário e nunca toca em privilégios de role de serviço. Qualquer coisa que ele possa fazer, um atacante não autenticado na internet também pode fazer.

De fora do banco de dados, um scanner pode confirmar o seguinte com alta confiança:

  • RLS está desabilitado em uma tabela. O PostgREST retorna linhas para um SELECT anônimo quando RLS está desligado ou quando uma política permite. Qualquer caso é um achado.
  • O role anônimo pode listar tabelas. Um GET /rest/v1/ com a chave anon retorna o schema OpenAPI para toda tabela em que o role anon tenha qualquer privilégio. Apps gerados por IA frequentemente concedem USAGE no schema e SELECT em cada tabela, o que expõe o mapa completo do schema mesmo quando RLS nega as leituras reais.
  • O role anônimo pode inserir. Um POST de sondagem com um chute na forma das colunas terá sucesso se RLS não tiver política INSERT que negue — mesmo se SELECT estiver bloqueado.
  • A chave de role de serviço está no bundle do navegador. Adjacente ao RLS: se um scanner encontrar SUPABASE_SERVICE_ROLE_KEY ou qualquer JWT com role: service_role no bundle JavaScript, o RLS é irrelevante — quem possui essa chave contorna qualquer política.

O que uma varredura externa não pode provar

Seja honesto sobre os limites do scanner. Uma varredura RLS externa não pode ler sua tabela pg_policies, seus arquivos de migração nem o predicado exato de nenhuma política. Ela infere de comportamento de caixa-preta, o que significa que às vezes vai reportar um achado que acaba sendo dado público intencional (tabela de newsletter de marketing, catálogo público de produtos). O relatório do FixVibe marca esses como confiança média quando o scanner não consegue desambiguar a intenção — revise o nome da tabela e decida.

As quatro formas de RLS quebrada que ferramentas de IA produzem

Quando você aponta Cursor, Claude Code, Lovable ou Bolt para o Supabase, os mesmos quatro padrões de RLS quebrada emergem em milhares de apps. Cada um passa na verificação de tipos, compila e é publicado:

Forma 1: RLS nunca habilitado

O modo de falha mais comum. A migração cria a tabela mas o desenvolvedor (ou a ferramenta de IA) esquece ALTER TABLE ... ENABLE ROW LEVEL SECURITY. O PostgREST serve alegremente a tabela inteira para qualquer um com a chave anon. Correção: ALTER TABLE public.[name] ENABLE ROW LEVEL SECURITY; ALTER TABLE public.[name] FORCE ROW LEVEL SECURITY;. FORCE não é opcional — sem ele, o dono da tabela (e qualquer role com propriedade de tabela) contorna o RLS.

sql
ALTER TABLE public.[name] ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.[name] FORCE  ROW LEVEL SECURITY;

Forma 2: RLS habilitado, sem políticas

Uma falha mais sutil. RLS está habilitado mas nenhuma política foi escrita. O padrão no PostgreSQL é negar, então usuários autenticados não veem nada — e o desenvolvedor adiciona USING (true) para o app funcionar, o que permite que todos leiam tudo. Correção: escreva uma política que restrinja por auth.uid(): CREATE POLICY "select_own" ON public.[name] FOR SELECT USING (auth.uid() = user_id); e uma política INSERT/UPDATE/DELETE correspondente.

sql
CREATE POLICY "select_own"
  ON public.[name]
  FOR SELECT
  USING (auth.uid() = user_id);

Forma 3: política compara coluna consigo mesma

A copy-paste artefact. The developer writes <code>USING (user_id = user_id)</code> — which is always true — instead of <code>USING (auth.uid() = user_id)</code>. Type-checks pass; the policy permits every row. <strong>Fix:</strong> always compare a column to a function call (<code>auth.uid()</code>, <code>auth.jwt()->>'org_id'</code>, etc.), never to itself or to a constant.

Forma 4: política em SELECT mas não em INSERT/UPDATE

O desenvolvedor bloqueia leituras mas esquece escritas. Políticas RLS são por comando. FOR SELECT protege apenas leituras; um cliente anônimo ainda pode INSERT se nenhuma política negar. Correção: escreva uma política por comando, ou use FOR ALL com cláusulas USING e WITH CHECK explícitas.

Como funciona o scanner de RLS Supabase do FixVibe

A verificação baas.supabase-rls roda em três estágios, cada um com níveis de confiança explícitos:

  1. Estágio 1 — fingerprinting. O scanner percorre o app publicado, analisa seu bundle JavaScript e extrai a URL do projeto Supabase e a chave anon da configuração de runtime. Sem adivinhação de DNS, sem força bruta — lê o que o navegador lê.
  2. Estágio 2 — descoberta de schema. Um único GET /rest/v1/ com a chave anon retorna o schema OpenAPI para cada tabela que o role anon pode ver. O scanner registra nomes de tabelas mas não lê dados de linhas neste estágio.
  3. Estágio 3 — sondagens de leitura e escrita. Para cada tabela descoberta, o scanner emite um SELECT anônimo com limit=1. Se linhas retornarem, RLS é permissivo. O scanner para aí — não enumera linhas, não pagina, não modifica dados. Sondagens INSERT são protegidas atrás de verificação de propriedade de domínio e opt-in explícito; nunca disparam contra alvos não verificados.

Cada achado vem com a URL exata da requisição, status da resposta, formato da resposta (apenas cabeçalho) e o nome da tabela. O prompt de correção com IA no final do achado é um bloco SQL pronto para colar que você executa no editor SQL do Supabase.

O que fazer quando o scanner encontra algo

Cada achado de RLS é uma emergência em produção. Endpoints PostgREST públicos são varridos por atacantes em minutos. A sequência de remediação é mecânica:

  1. Audite cada tabela. Execute SELECT schemaname, tablename, rowsecurity FROM pg_tables WHERE schemaname = 'public'; no editor SQL do Supabase. Qualquer linha com rowsecurity = false é problema.
  2. Habilite RLS em cada tabela pública. Por padrão, aplique ENABLE ROW LEVEL SECURITY e FORCE ROW LEVEL SECURITY em cada tabela criada — torne isso um template de migração.
  3. Escreva políticas comando por comando. Não use FOR ALL USING (true). Escreva políticas explícitas para SELECT, INSERT, UPDATE, DELETE — cada uma restrita a auth.uid() ou a uma coluna de org-id de auth.jwt().
  4. Verifique com uma segunda conta. Cadastre-se como outro usuário, tente ler os registros de outro usuário via API REST diretamente. Se a resposta for 200, a política está quebrada.
  5. Varra de novo. Após aplicar a correção, rode novamente uma varredura do FixVibe contra a mesma URL. O achado baas.supabase-rls deve desaparecer.
sql
-- Audit every table for missing RLS. Run in the Supabase SQL editor.
SELECT schemaname, tablename, rowsecurity
FROM   pg_tables
WHERE  schemaname = 'public'
ORDER  BY rowsecurity, tablename;

Como isso se compara a outros scanners

A maioria das ferramentas DAST genéricas (Burp Suite, OWASP ZAP, Nessus) não sabe o que é PostgREST. Elas vão percorrer seu app, ignorar o caminho /rest/v1/ e reportar as páginas HTML que entendem. Snyk e Semgrep são ferramentas de análise estática — encontram arquivos de migração no seu repositório com chamadas RLS ausentes, mas não conseguem provar que o banco publicado está mal configurado. O FixVibe fica nessa lacuna: passivo, ciente de BaaS, focado no que um atacante não autenticado consegue provar a partir da URL pública.

Perguntas frequentes

O scanner vai ler ou modificar meus dados?

Não. Varreduras passivas emitem no máximo um SELECT ... limit=1 por tabela descoberta para confirmar se RLS permite leituras anônimas. O scanner registra o formato da resposta, não o conteúdo das linhas. Sondagens INSERT, UPDATE e DELETE são protegidas atrás de verificação de propriedade de domínio e nunca rodam contra alvos não verificados.

Isso funciona se meu projeto Supabase estiver pausado ou em domínio personalizado?

Projetos pausados retornam 503 em cada requisição — o scanner reporta o projeto como inalcançável. Domínios personalizados funcionam desde que o app publicado ainda carregue o SDK cliente Supabase no navegador; o scanner extrai a URL do projeto do bundle de qualquer forma.

E se minha chave anon for rotacionada ou minha chave publicável mudar?

Rode a varredura de novo. O scanner re-extrai a chave do bundle atual a cada execução. A rotação invalida apenas o relatório anterior, não o estado das políticas do banco.

O scanner verifica o novo modelo de chave publicável Supabase (<code>sb_publishable_*</code>)?

Sim. O detector reconhece tanto os JWTs anon antigos quanto as chaves mais novas sb_publishable_* e as trata identicamente — ambas são feitas para serem públicas e ambas deixam RLS como única linha de defesa.

Próximos passos

Rode uma varredura gratuita do FixVibe contra sua URL de produção — a verificação baas.supabase-rls está habilitada em todos os planos, incluindo o gratuito. Para leitura mais profunda sobre o que mais pode vazar de um projeto Supabase, veja Chave de role de serviço Supabase exposta em JavaScript e Checklist de segurança de buckets Supabase Storage. Para a visão geral entre todos os provedores BaaS, leia Scanner de configurações incorretas de BaaS.

// 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

Scanner de RLS Supabase: encontre tabelas com segurança em nível de linha ausente ou quebrada — Docs · FixVibe