Supabase Row Level Security (RLS): proteggi le tue tabelle
Supabase espone le tue tabelle Postgres direttamente al browser tramite la sua Data API, autenticata con la anon key (publishable) pubblica. Le policy di Row Level Security (RLS) decidono quali righe ogni utente può leggere o modificare. Una tabella nello schema public senza RLS è leggibile — e spesso scrivibile — da chiunque apra il tuo sito.
Che cos'è
Ogni progetto Supabase include una anon key (o sb_publishable_) pensata per stare nel frontend. Identifica il tuo progetto, non il tuo utente: chiunque può copiarla dal bundle JavaScript e inviare richieste a https://<project>.supabase.co/rest/v1/ in autonomia, scavalcando completamente la tua UI.
La Row Level Security è una funzionalità di Postgres che associa delle policy a una tabella: condizioni SQL verificate per ogni riga a ogni SELECT, INSERT, UPDATE e DELETE. Con la RLS attiva e nessuna policy, nulla è accessibile tramite l'API. Ogni policy che aggiungi apre esattamente una porta, ad esempio "gli utenti autenticati possono leggere le righe in cui user_id corrisponde al proprio id".
Perché è importante
Una policy mancante o troppo permissiva è una violazione completa dei dati, non un bug minore: email degli utenti, profili, ordini, messaggi privati — qualunque cosa ci sia nella tabella — possono essere scaricati con un singolo comando curl. Gli strumenti di coding AI creano spesso le tabelle tramite migrazioni SQL, dove la RLS è disattivata di default, e generano policy segnaposto permissive come using (true) solo per far funzionare l'app.
La chiave service_role (sb_secret_) scavalca completamente la RLS. Se finisce nel codice lato client, ogni policy che hai scritto diventa irrilevante: chi la possiede ha pieno accesso in lettura e scrittura al tuo database.
Come risolverlo
Esegui questa query nel SQL editor di Supabase per elencare ogni tabella dello schema public esposto che ha ancora la RLS disattivata. Il Security Advisor nella dashboard segnala lo stesso problema come "RLS disabled in public".
select tablename
from pg_tables
where schemaname = 'public'
and rowsecurity = false;Attiva la RLS su ogni tabella, poi aggiungi una policy per ciascuna operazione. Limita le policy al ruolo authenticated e confronta la colonna del proprietario della riga con auth.uid(). Insert e update richiedono with check, così gli utenti non possono scrivere righe che appartengono ad altri.
alter table public.profiles enable row level security;
create policy "Users read own profile"
on public.profiles for select
to authenticated
using ((select auth.uid()) = user_id);
create policy "Users update own profile"
on public.profiles for update
to authenticated
using ((select auth.uid()) = user_id)
with check ((select auth.uid()) = user_id);Sostituisci le policy segnaposto come using (true) con condizioni reali. Non basare l'autorizzazione su user_metadata, che gli utenti possono modificare da soli: usa invece app_metadata o una tabella dei ruoli. Le view scavalcano la RLS a meno che non siano create con security_invoker = true, e i bucket di storage richiedono policy proprie su storage.objects.
Interroga la tua API esattamente come farebbe un malintenzionato: solo con la chiave pubblica e senza alcuna sessione utente. Se la richiesta restituisce righe che non dovrebbero essere pubbliche, le tue policy sono troppo aperte.
curl "https://<project>.supabase.co/rest/v1/profiles?select=*" \
-H "apikey: <anon-key>" \
-H "Authorization: Bearer <anon-key>"Usa la chiave service_role (sb_secret_) solo nel codice server, come route API o Edge Functions, mai dietro un prefisso NEXT_PUBLIC_ o VITE_. Se è mai arrivata al browser, ruotala dalla dashboard.
FAQ
È sicuro che la mia anon key di Supabase sia visibile nel browser?
Sì: la anon key (publishable) è progettata per essere pubblica, ma solo se ogni tabella esposta ha la RLS attiva con policy corrette. La chiave di per sé non concede nulla che le tue policy non consentano.
Ho attivato la RLS e ora la mia app restituisce risultati vuoti. È rotta?
È la RLS che funziona come previsto: senza policy, ogni riga viene negata. Aggiungi policy per le operazioni di cui la tua app ha bisogno, limitate agli utenti giusti, invece di disattivare di nuovo la RLS.
AppSafe testa le mie policy RLS?
AppSafe non accede al tuo database, quindi non può valutare le tue policy: usa i controlli SQL e curl qui sopra. Analizza però il tuo JavaScript alla ricerca di chiavi Supabase. Se ne viene segnalata una, decodifica la parte centrale del JWT e controlla il claim role: anon è normale nel browser, service_role è una fuga critica.
La tua app è interessata?
AppSafe controlla questo e decine di altri problemi in un'unica scansione gratuita.
Scansiona la mia app gratisGuide correlate
Firebase Security Rules: smetti di lasciare aperto il tuo database
La tua config Firebase è pubblica per design. Sono le regole a decidere chi vede i dati.
API key esposte nel JavaScript lato client
Tutto ciò che è nel tuo bundle è pubblico. Trattalo come tale.
Backup del database esposti: cosa succede quando /backup.sql è raggiungibile
Un dump .sql raggiungibile equivale a consegnare l'intero database.