// 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
SELECTanó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 roleanontenha qualquer privilégio. Apps gerados por IA frequentemente concedemUSAGEno schema eSELECTem 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
POSTde sondagem com um palpite na forma das colunas terá sucesso se RLS não tiver políticaINSERTque negue — mesmo seSELECTestiver bloqueado. - A chave de role de serviço está no bundle do navegador. Adjacente ao RLS: se um scanner encontrar
SUPABASE_SERVICE_ROLE_KEYou qualquer JWT comrole: service_roleno 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.
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.
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:
- 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ê.
- 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. - Etapa 3 — sondagens de leitura e escrita. Para cada tabela descoberta, o scanner emite um
SELECTanónimo comlimit=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:
- Audite cada tabela. Execute
SELECT schemaname, tablename, rowsecurity FROM pg_tables WHERE schemaname = 'public';no editor SQL do Supabase. Qualquer linha comrowsecurity = falseé problema. - Habilite RLS em cada tabela pública. Por defeito, aplique
ENABLE ROW LEVEL SECURITYeFORCE ROW LEVEL SECURITYem cada tabela criada — torne isso um template de migração. - 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 aauth.uid()ou a uma coluna de org-id deauth.jwt(). - 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. - Varra de novo. Após aplicar a correção, execute novamente um varrimento do FixVibe contra a mesma URL. O achado
baas.supabase-rlsdeve desaparecer.
-- 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.
