Vai al contenuto

Supabase security definer view: perché aggira la RLS

Supabase

Visualizza come MarkdownApri in ClaudeApri in ChatGPTApri in Perplexity

Anche la domanda viene copiata. Se la chat si apre vuota, la incolli.

In breve

Una vista Postgres gira con i diritti del suo proprietario, a meno che non sia creata con security_invoker = true. In Supabase il proprietario è di solito postgres, che aggira la RLS: una vista su una tabella protetta restituisce quindi righe che la tabella stessa nasconde. Sul mio stack locale la chiave pubblica ha ottenuto 0 righe dalla tabella e 3 su 3 da una vista semplice sopra di essa; con security_invoker = true anche la vista ne ha restituite 0. Da Postgres 15 in poi, lo imposti su ogni vista di uno schema esposto.

Il mio checker di sicurezza per Supabase gratuito ha una regola chiamata “View che aggirano RLS”, e il Security Advisor di Supabase segnala la stessa cosa come security_definer_view. È facile non vederla, perché la tabella da cui perde sembra protetta.

Perché una vista Supabase aggira la RLS?

Una vista è una query salvata. Di default Postgres controlla permessi e RLS delle tabelle al suo interno come proprietario della vista, non come la persona che la chiama. La guida alla RLS di Supabase lo dice senza giri di parole: le viste aggirano la RLS di default perché di solito sono create con l’utente postgres, e una vista su una tabella protetta consegna ogni riga che le sue policy dovevano trattenere.

La Data API serve le viste come tabelle. Se la vista è in uno schema esposto e anon o authenticated possono leggerla, la legge la chiave pubblica.

Cosa restituiva la vista nel mio test?

Su uno stack Supabase locale (CLI 2.109.1, Postgres 17.6, PostgREST 14.14) ho creato una tabella orders con tre clienti finti, attivato la RLS senza policy e creato sopra una vista order_summary con un grant select per anon. Poi ho chiamato entrambe con la publishable key:

Stessa tabella, tre modi di leggerla. Supabase locale, RLS attiva senza policy, publishable key. La tabella stessa: Publishable key → orders, RLS attiva → 0 righe; Una vista semplice sopra di essa: Publishable key → vista order_summary → 3 righe su 3; La vista con security_invoker: Publishable key → vista security_invoker → 0 righe.
La mia riproduzione su uno stack Supabase locale, 23 ottobre 2026.
Richiesta con la publishable key Risultato
GET /rest/v1/orders (RLS attiva, nessuna policy) []
GET /rest/v1/order_summary (vista semplice) tutte le 3 righe, con nomi ed email
GET /rest/v1/order_summary dopo security_invoker = true []

Stessa tabella, stessa chiave. Solo l’impostazione della vista ha cambiato ciò che tornava.

Come si risolve?

Da Postgres 15 in poi, su cui gira ogni progetto Supabase attuale, faccia controllare la RLS alla vista come chi la chiama:

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

Per una nuova vista lo metta nella definizione:

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

Chi chiama deve allora avere anche select sulla tabella sottostante, e sono le policy della tabella a decidere le righe. Se una vista deve aggregare dati che chi chiama non può vedere riga per riga, come un conteggio pubblico, la tenga fuori dallo schema esposto e la serva invece tramite una funzione con un risultato ristretto.

Per elencare le viste di public che girano ancora come il loro proprietario:

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);

Sul mio stack, con una vista semplice e due con security_invoker = true, ha restituito solo quella semplice.

Le viste materializzate non supportano affatto la RLS, quindi il checker le segnala a parte, sotto “Viste materializzate o foreign table nell’API”. Tutto il resto di ciò che passa oltre le policy, dalla secret key alle funzioni SECURITY DEFINER, è nella mia checklist di sicurezza per le app Lovable, e come dimostrare le policy stesse è in come testare la Supabase RLS. Una policy che cerca i team tramite una funzione di supporto, senza ricorsione, è in Esempi di Supabase RLS policy, testati tramite API. Una funzione security definer richiede un’altra impostazione, un search_path fisso; perché search_path = public non bastava nel mio test è in Supabase Function Search Path Mutable: cosa significa.

© 2026 Denys Zaichenko. Articolo originale: https://madsalt.tech/it/notes/supabase-security-definer-view/