Saltar para o conteúdo

Supabase security definer view: porque ignora o RLS

Supabase

Ver como MarkdownAbrir em ClaudeAbrir em ChatGPTAbrir em Perplexity

A pergunta também é copiada. Se o chat abrir vazio, cole-a.

Em resumo

Uma vista do Postgres corre com os direitos do seu dono, a menos que seja criada com security_invoker = true. No Supabase o dono costuma ser o postgres, que ignora o RLS, por isso uma vista sobre uma tabela protegida devolve linhas que a própria tabela esconde. Na minha stack local a chave pública obteve 0 linhas da tabela e 3 de 3 de uma vista simples sobre ela; com security_invoker = true a vista também devolveu 0. No Postgres 15 e posteriores, ative-o em todas as vistas de um schema exposto.

O meu verificador de segurança do Supabase gratuito tem uma regra chamada “Vistas que contornam o RLS”, e o Security Advisor do Supabase reporta o mesmo como security_definer_view. É fácil passar-lhe ao lado, porque a tabela de onde os dados saem parece protegida.

Porque é que uma vista do Supabase ignora o RLS?

Uma vista é uma consulta guardada. Por omissão, o Postgres verifica as permissões e o RLS das tabelas lá dentro como o dono da vista, não como quem a chama. O guia de RLS do Supabase di-lo sem rodeios: as vistas ignoram o RLS por omissão porque costumam ser criadas com o utilizador postgres, e uma vista sobre uma tabela protegida entrega todas as linhas que as suas políticas deviam reter.

A Data API serve as vistas como se fossem tabelas. Se a vista está num schema exposto e o anon ou o authenticated podem fazer select nela, a chave pública lê-a.

O que devolveu a vista no meu teste?

Numa stack local do Supabase (CLI 2.109.1, Postgres 17.6, PostgREST 14.14) criei uma tabela orders com três clientes fictícios, ativei o RLS sem política nenhuma e criei uma vista order_summary sobre ela com um grant de select para o anon. Depois chamei as duas com a publishable key:

A mesma tabela, três formas de a ler. Supabase local, RLS ligado sem política, publishable key. A própria tabela: Publishable key → orders, RLS ligado → 0 linhas; Uma vista simples sobre ela: Publishable key → vista order_summary → 3 de 3 linhas; A vista com security_invoker: Publishable key → vista security_invoker → 0 linhas.
A minha reprodução numa stack local do Supabase, 23 de outubro de 2026.
Pedido com a publishable key Resultado
GET /rest/v1/orders (RLS ligado, sem política) []
GET /rest/v1/order_summary (vista simples) as 3 linhas, com nomes e emails
GET /rest/v1/order_summary depois de security_invoker = true []

A mesma tabela, a mesma chave. Só a definição da vista mudou o que voltou.

Como se corrige?

No Postgres 15 e posteriores, que é o que qualquer projeto Supabase atual usa, faça a vista verificar o RLS como quem a chama:

alter view public.order_summary set (security_invoker = true);

Numa vista nova, ponha-o na definição:

create view public.order_summary
with (security_invoker = true)
as select customer, email, total from public.orders;

Quem chama passa então a precisar também de select na tabela subjacente, e as políticas da tabela decidem as linhas. Se uma vista tiver de agregar dados que quem chama não pode ver linha a linha, como uma contagem pública, mantenha-a fora do schema exposto e sirva-a através de uma função com um resultado restrito.

Para listar as vistas de public que ainda correm como o seu dono:

select c.relname as view
from pg_class c join pg_namespace n on n.oid = c.relnamespace
where n.nspname = 'public' and c.relkind = 'v'
  and not coalesce(
    (select bool_or(option = 'security_invoker=true' or option = 'security_invoker=on')
     from unnest(c.reloptions) as option), false);

Na minha stack, com uma vista simples e duas com security_invoker = true, devolveu só a simples.

As vistas materializadas não suportam RLS de todo, por isso o verificador assinala-as à parte na API, em “Vistas materializadas ou tabelas externas na API”. O que mais contorna as políticas, da secret key às funções SECURITY DEFINER, está na minha lista de segurança para apps Lovable, e como provar as próprias políticas está em como testar o Supabase RLS. Uma policy que procura equipas através de uma função auxiliar, sem recursão, está em Exemplos de RLS policy no Supabase, testados pela API. Uma função security definer precisa de mais uma definição, um search_path fixo; porque search_path = public não bastou no meu teste está em Supabase “function search path mutable”: o que significa.

© 2026 Denys Zaichenko. Artigo original: https://madsalt.tech/pt/notes/supabase-security-definer-view/