In breve
“RLS Disabled in Public” (lint 0013_rls_disabled_in_public, livello ERROR) significa che una tabella in uno schema esposto ha la row level security disattivata mentre anon o authenticated può leggerla con select. Nel mio test locale la chiave pubblica ha letto tutte e 3 le righe su 3 di una tale tabella e ne ha inserita una nuova con 201 Created. Attivi la RLS, aggiunga una policy per operazione e controlli i grant, tutto in un’unica migrazione.
Ho costruito un checker di sicurezza per Supabase gratuito, e la sua prima regola, “RLS disattivato su tabelle esposte”, è lo stesso rilievo che il Security Advisor di Supabase segnala come errore. È la falla più comune che trovo nelle app costruite con strumenti di IA, quindi ecco cosa significa e cosa permetteva quando l’ho riprodotta.
Cosa significa “RLS disabled in public”?
Il Security Advisor esegue un lint chiamato rls_disabled_in_public. Il suo SQL segnala una tabella quando sono vere tre cose insieme:
- È una tabella ordinaria con la row level security disattivata.
anonoauthenticatedhaSELECTsu di essa.- Il suo schema è nell’elenco Exposed schemas della Data API, che di default è
public.
La pagina del lint lo classifica ERROR e definisce “a critically unsafe configuration” la possibilità di creare, leggere, modificare ed eliminare tutto via Internet. Il “public” nel nome è lo schema, ma il lint copre ogni schema esposto, per questo anche uno schema in più in quell’elenco merita un’occhiata; ne ho scritto in Supabase exposed schemas.
Cosa può fare la chiave pubblica a una tale tabella?
Ho creato una tabella orders con tre clienti finti su uno stack Supabase locale (CLI 2.109.1, Postgres 17.6, PostgREST 14.14), con i grant che i progetti esistenti danno alle nuove tabelle, e la RLS disattivata. Poi ho chiamato la Data API con la publishable key, come fa un browser:
| Richiesta con la publishable key | RLS disattivata | RLS attiva, nessuna policy |
|---|---|---|
GET /rest/v1/orders?select=* |
200, tutte e 3 le righe con nomi ed email |
200, [] |
POST /rest/v1/orders con una nuova riga |
201 Created |
401, 42501 “new row violates row-level security policy” |
Chiunque apra la sua app può copiare la publishable key dalla pagina. Con la RLS disattivata, la chiave è un lasciapassare in lettura e scrittura su quella tabella.
Come si corregge senza rompere l’app?
Attivare soltanto la RLS fa sì che la tabella non restituisca nulla all’app, perché non c’è ancora una policy. Quello schermo vuoto è il momento in cui le persone, e gli strumenti di IA, aggiungono una policy con using (true), che restituisce ogni riga a tutti. Perché update e delete segnalano allora un successo senza cambiare nulla è spiegato in Attivare la RLS in Supabase: cosa restituisce ogni passo. Scriva le vere policy nella stessa migrazione:
alter table public.orders enable row level security;
revoke all on table public.orders from anon, authenticated;
grant select, insert on table public.orders to authenticated;
create policy "Customers read their orders"
on public.orders for select
to authenticated
using ((select auth.uid()) = user_id);
create policy "Customers create their orders"
on public.orders for insert
to authenticated
with check ((select auth.uid()) = user_id);
Se una tabella deve essere pubblica, come un elenco di articoli pubblicati, tenga comunque la RLS attiva e scriva una sola policy select per anon, senza grant di scrittura. Se non usa mai la Data API e parla con Postgres solo dal suo server, rimuova invece public da Exposed schemas, come hanno suggerito i maintainer di Supabase nella stessa issue.
Perché le nuove tabelle non partano mai senza RLS, la guida agli event trigger di Supabase ha un trigger che la attiva su ogni tabella creata in public. E dal 30 ottobre 2026 le nuove tabelle non ricevono più grant per la Data API per default, quindi rispondono 42501 finché non li concede; l’ho trattato in Supabase 42501 permission denied.
Cosa non dimostra un risultato pulito?
Che la RLS sia attiva non dice nulla sulla correttezza delle policy. Nello stesso test una policy scritta using (true) ha restituito di nuovo tutte e 3 le righe alla chiave pubblica, e questo lint tace una volta che la RLS è attiva. Il checker ha una regola a parte per questo, “Policy che consentono ogni riga”, e come testare la Supabase RLS mostra come dimostrare ogni policy con utenti reali. Policy da cui partire, ciascuna eseguita con utenti reali tramite l’API, sono in Esempi di Supabase RLS policy, testati tramite API.