FixVibe

// docs / baas security / supabase rls scanner

Scanner de RLS Supabase: encontre tabelas com segurança ao nível da linha ausente ou avariada

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

O que um varrimento RLS externo pode provar

Um varrimento RLS passivo executa 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 o seu navegador usa — e sonda metadados de listagem de tabelas, leituras anónimas e escritas anónimas. Nunca se autentica como utilizador e nunca toca em privilégios de role de serviço. Qualquer coisa que possa fazer, um atacante não autenticado na internet também pode fazer.

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

  • RLS está desativado numa tabela. O PostgREST devolve 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 devolve o schema OpenAPI para toda a 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 palpite 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 um varrimento externo não pode provar

Seja honesto sobre os limites do scanner. Um varrimento RLS externo não pode ler a sua tabela pg_policies, os seus ficheiros de migração nem o predicado exato de nenhuma política. Ele infere de comportamento de caixa-preta, o que significa que às vezes vai reportar um achado que acaba por ser 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 — reveja o nome da tabela e decida.

As quatro formas de RLS avariado que ferramentas de IA produzem

Quando aponta Cursor, Claude Code, Lovable ou Bolt para o Supabase, os mesmos quatro padrões de RLS avariado 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 programador (ou a ferramenta de IA) esquece ALTER TABLE ... ENABLE ROW LEVEL SECURITY. O PostgREST serve alegremente a tabela inteira a 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 subtil. RLS está habilitado mas nenhuma política foi escrita. O padrão no PostgreSQL é negar, então utilizadores autenticados não veem nada — e o programador adiciona USING (true) para a 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 própria

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 programador 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 corre em três etapas, cada uma com níveis de confiança explícitos:

  1. Etapa 1 — fingerprinting. O scanner percorre a app publicada, analisa o 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. Etapa 2 — descoberta de schema. Um único GET /rest/v1/ com a chave anon devolve o schema OpenAPI para cada tabela que o role anon consegue ver. O scanner regista nomes de tabelas mas não lê dados de linhas nesta etapa.
  3. Etapa 3 — sondagens de leitura e escrita. Para cada tabela descoberta, o scanner emite um SELECT anónimo com limit=1. Se linhas regressarem, RLS é permissivo. O scanner para aí — não enumera linhas, não pagina, não modifica dados. Sondagens INSERT estã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 do pedido, estado da resposta, formato da resposta (apenas cabeçalho) e o nome da tabela. O prompt de correção com IA no fundo do achado é um bloco SQL pronto a colar que 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 defeito, 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 a 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. Registe-se como outro utilizador, tente ler os registos de outro utilizador via API REST diretamente. Se a resposta for 200, a política está avariada.
  5. Varra de novo. Após aplicar a correção, execute novamente um varrimento 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 isto se compara a outros scanners

A maioria das ferramentas DAST genéricas (Burp Suite, OWASP ZAP, Nessus) não sabe o que é PostgREST. Vão percorrer a sua 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 ficheiros de migração no seu repositório com chamadas RLS em falta, mas não conseguem provar que a base de dados publicada está mal configurada. 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 os meus dados?

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

Isto funciona se o meu projeto Supabase estiver em pausa ou em domínio personalizado?

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

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

Execute o varrimento de novo. O scanner re-extrai a chave do bundle atual em cada execução. A rotação invalida apenas o relatório anterior, não o estado das políticas da base de dados.

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

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

Próximos passos

Execute um varrimento gratuito do FixVibe contra a 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 fornecedores BaaS, leia Scanner de configurações incorretas de BaaS.

// varra a 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 fornecedores BaaS com que a sua app fala, identifica os seus endpoints públicos e reporta o que um cliente não autenticado pode ler ou escrever. Grátis, sem instalação, sem cartão.

  • Plano gratuito — 3 varrimentos/mês, sem cartão de registo.
  • 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.
Executar varrimento BaaS gratuito

sem necessidade de registo

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