Saltar para o conteúdo

Supabase exposed schemas: o que a lista expõe

Supabase

Ver como MarkdownAbrir em ClaudeAbrir em ChatGPTAbrir em Perplexity

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

Em resumo

A lista Exposed schemas do Supabase decide que schemas a Data API serve a quem tiver a sua chave pública. Por omissão são public e graphql_public. Cada tabela, vista e função de um schema da lista precisa dos seus próprios grants e de RLS, por isso cada schema extra é mais superfície a defender. Mantenha a lista curta e verifique-a primeiro.

Criei um verificador de segurança do Supabase gratuito, e a lista Exposed schemas é uma das primeiras coisas que ele lê. Quando alguém pergunta se um projeto Supabase é seguro, é por esta definição que começo.

O que significa schema exposto no Supabase?

O Supabase serve a sua base de dados por REST com o PostgREST. O PostgREST só responde pelos schemas da sua lista db_schema, que se edita no Dashboard em Project Settings → Data API → Exposed schemas. Na configuração do projeto que vi, o valor era public,graphql_public, e o guia de schemas personalizados do Supabase diz que o public é exposto por omissão. Um pedido a um schema fora da lista é recusado, e o PostgREST nunca permite pg_catalog nem information_schema nela.

Porque é que a lista importa para a segurança do Supabase?

Tudo o que está num schema exposto é alcançável com a chave pública: tabelas, vistas e funções. O guia de RLS do Supabase é direto: uma tabela num schema exposto sem RLS pode ser lida e escrita por qualquer função com um grant sobre ela. Um schema que não está na lista não pode ser alcançado de todo pela API.

Por isso, um schema extra não é uma alteração neutra. Junte storage ou vault à lista e coloca tabelas internas ao alcance da Data API. Junte o seu próprio schema e cada tabela dele precisa do mesmo cuidado que o public.

O que abre um schema exposto a mais. Um pedido anon, antes e depois de reduzir a lista. Antes: um schema extra exposto: pedido anon → Data API → schema extra → tabela sem RLS legível; Depois: só public e graphql_public: pedido anon → Data API → só schemas por omissão → RLS em cada tabela.
Das regras do verificador de segurança do Supabase e da documentação da Data API do Supabase, 22 de outubro de 2026.

Como é que o verificador classifica a lista?

A verificação chama-se extra_exposed_schemas. Tudo o que não for public nem graphql_public conta como extra, e depois:

  • Um schema interno do Supabase na lista (auth, storage, realtime, vault, extensions e semelhantes) é uma falha, gravidade alta.
  • Só schemas personalizados, por exemplo api, dão um aviso, gravidade média.
  • Sem extras, passa.

A mesma lista alimenta outras verificações. Tabelas com RLS desligado, grants de escrita ao anon, políticas sempre verdadeiras, vistas sem security_invoker e funções SECURITY DEFINER que o anon pode chamar só são reportadas para schemas expostos, porque é aí que a chave pública lá chega.

Como usar um schema personalizado em segurança?

Um schema personalizado é um padrão legítimo: o guia Securing your API do Supabase descreve um schema dedicado, como api, como forma de tornar a superfície exposta mais fácil de identificar e auditar, com as tabelas internas em schemas que não são expostos. Dois passos fazem-no funcionar, e o guia de schemas personalizados dá os dois: juntar o schema a Exposed schemas e depois conceder usage sobre ele a anon, authenticated e service_role, mais privilégios sobre os seus objetos.

O exemplo do guia concede ALL em todas as tabelas. Eu não copiaria essa linha tal como está. O guia Securing your API manda dar a cada função o mínimo de que precisa e ativar o RLS em todas as tabelas que expõe. Os grants decidem se uma função pode tocar numa tabela, as políticas decidem que linhas.

Uma alteração à parte: o Supabase deixa de conceder automaticamente à Data API as novas tabelas de public. Passou a ser o padrão nos projetos novos desde 30 de maio de 2026 e será aplicado aos projetos existentes em 30 de outubro de 2026, e as tabelas existentes mantêm os grants. Essa é a camada dos grants. Não muda os schemas que estão na lista.

Como verificar o seu projeto?

Abra Project Settings → Data API e leia Exposed schemas. Se for mais do que public e graphql_public, pergunte para que serve cada schema extra.

Depois liste as tabelas desses schemas que têm o RLS desligado, e se o anon já as pode ler:

select n.nspname as schema,
       c.relname as "table",
       has_table_privilege('anon', c.oid, 'select') as anon_can_select
from pg_class c
join pg_namespace n on n.oid = c.relnamespace
where n.nspname in ('public', 'api')  -- your Exposed schemas
  and c.relkind in ('r', 'p')
  and not c.relrowsecurity
order by 1, 2;

Corri-a num Postgres 17 descartável, com um schema api que tinha uma tabela com RLS e outra sem, mais uma tabela public sem RLS. Devolveu só as duas sem:

 schema | table  | anon_can_select
--------+--------+-----------------
 api    | orders | t
 public | logs   | t

Cada linha com anon_can_select verdadeiro é uma tabela que qualquer pessoa com a sua chave pública consegue ler, até ativar o RLS ou revogar o grant. A consulta cobre apenas tabelas. As vistas e as funções pedem a sua própria análise, que é o que a minha lista de segurança para apps Lovable percorre, e como testo o Supabase RLS mostra como provar que as políticas funcionam.

© 2026 Denys Zaichenko. Artigo original: https://madsalt.tech/pt/notes/supabase-exposed-schemas/