16.326 database Supabase leggibili da chiunque. È il conteggio che UpGuard ha pubblicato il 25 settembre 2026 dopo aver interrogato circa 300.000 domini che usano Supabase: in più della metà c’erano indizi di dati personali. In un caso, un servizio di consulenza per chi si trasferisce in Canada, quasi 5.000 utenti con nome, email, telefono e data di nascita, e 884 password salvate in chiaro.

Nessuno ha bucato Supabase. Quei dati erano esposti per configurazione, e la configurazione dipende da una decisione che molti non sanno di aver preso: dove si controlla chi può leggere cosa. Le strade sono due, e questo articolo le confronta.

Le due architetture a confronto

A: il browser parla direttamente con il database. L’app usa la chiave pubblicabile di Supabase, che finisce nel JavaScript servito a tutti, e la sicurezza sta nelle policy RLS di Postgres: regole scritte dentro il database che decidono, riga per riga, cosa vede chi fa la richiesta. È il modello che generano gli assistenti di coding.

B: il browser parla con il tuo server. Tra la pagina e il database c’è uno strato tuo: una Edge Function, un endpoint sul tuo backend, una route REST di WordPress. La chiave segreta vive solo lì, il controllo di chi può fare cosa è codice tuo, e il database non è raggiungibile dal client per le tabelle sensibili.

Il criterio che uso per confrontarle è uno solo: cosa succede quando qualcuno sbaglia. Sulla carta sono sicure entrambe: conta quale errore è più probabile e quanto costa scoprirlo.

Opzione A: RLS e chiave pubblicabile

Il vantaggio è reale: niente backend da scrivere, e l’autorizzazione sta vicino ai dati e vale per ogni client, app mobile compresa. Per un prototipo è la strada più veloce.

Il problema è il modo in cui fallisce: aperto. La documentazione di Supabase lo scrive senza giri di parole: una tabella in uno schema esposto senza RLS è leggibile e scrivibile da qualunque ruolo abbia i permessi su di essa. Secondo l’analisi di UpGuard, Supabase oggi attiva RLS di default solo per le tabelle create dall’editor visuale, mentre quelle create da codice, cioè come lavorano gli agenti di coding, nascono senza. Una tabella dimenticata non dà errori e non rompe l’app: funziona benissimo, per tutti.

E attivare RLS non basta. Serve una policy per ogni operazione, vanno revocati i permessi che i ruoli anon e authenticated possono avere già di default, e le viste vanno trattate a parte perché di default aggirano le policy delle tabelle sottostanti. La procedura minima per una tabella è questa:

alter table public.clienti enable row level security;
revoke all on table public.clienti from anon, authenticated;
grant select on table public.clienti to authenticated;
-- poi una policy per operazione e un test in supabase/tests/

La guida di Supabase lo mette per iscritto: finché la suite di test delle policy non passa, non sai se le policy fanno quello che volevi. È onesto, ed è il punto: la sicurezza di A è buona quanto la disciplina di chi la configura.

Opzione B: API sul server e chiave segreta

Qui il fallimento di default è chiuso. Se dimentichi di esporre una tabella, il client non la vede e l’app si rompe in modo visibile: te ne accorgi in sviluppo, non dal giornale. Il controllo degli accessi sta in un posto solo e si testa come il resto del codice.

Il costo è il lavoro in più: ogni lettura e scrittura passa da un endpoint da scrivere e mantenere. E si sposta il rischio su un’altra chiave. La chiave segreta aggira tutte le policy RLS: la guida alle chiavi avverte che una chiave segreta trapelata espone tutti i dati del progetto. Le chiavi segrete nuove hanno almeno un paracadute, perché Supabase riconosce le richieste dal browser dallo User-Agent e risponde 401, ma una chiave committata su un repository pubblico resta un disastro completo.

Pro e contro in una riga

Criterio A: RLS dal browser B: API sul server
Messa in opera Rapida, niente backend Più lenta, un endpoint per operazione
Errore tipico Tabella senza policy Chiave segreta nel posto sbagliato
Come fallisce Aperto e silenzioso Chiuso e visibile
Quando cresce Ogni tabella nuova è un rischio Ogni endpoint nuovo è codice da mantenere
Chi può verificarlo Chi legge SQL e policy Chi legge il codice del backend

Cosa cambia per una PMI e per chi lavora con WordPress

Il dato più scomodo della ricerca è un altro: UpGuard non ha trovato differenze tra siti B2B e B2C nel tipo di dati esposti. Chi gestisce l’attività sa benissimo che dati tratta, ma non sa come è configurato il suo database, perché quella parte l’ha scritta un agente. La stessa ricerca richiama il caso di Lovable del marzo 2025 (CVE-2025-48757) e uno studio di Modern Pentest su 107 startup di Y Combinator, il 28% delle quali esponeva dati personali. Non è un problema di principianti isolati.

Chi lavora con WordPress ha già in casa l’opzione B. Una route REST con un permission_callback vero, la chiave segreta in wp-config.php e mai nel tema o in un file JavaScript, e il browser che non vede Supabase per niente. Se un cliente vi porta un’app fatta con un assistente AI e un modulo che scrive su Supabase, la prima cosa da cercare è la chiave nel sorgente della pagina, la seconda è l’elenco delle tabelle con RLS disattivato. Ne ho scritto dal lato del processo in vibe coding: prototipo o produzione, e vale lo stesso principio che applico agli accessi REST dati a un esterno: ogni credenziale deve avere un posto preciso e un responsabile.

Il CISO di Supabase, Bil Harmer, ha risposto a TechCrunch che i progetti sono sicuri per impostazione predefinita e che la sicurezza è una responsabilità condivisa con i clienti. Tecnicamente regge. Ma se l’impostazione predefinita dipende da quale strumento ha creato la tabella, la parte condivisa finisce tutta su chi non sa di averla.

La mia scelta

Per una PMI, B. Tutte le tabelle con dati personali, pagamenti o documenti passano da un endpoint sul server, e il browser usa la chiave pubblicabile solo per ciò che è davvero pubblico. RLS resta attivo ovunque come secondo livello, non come unico.

A ha senso in un caso preciso: un team che scrive le policy a mano, le versiona in una migrazione e le prova con supabase test db a ogni rilascio. Se le policy le ha scritte un assistente e nessuno le ha mai testate, non si sta usando RLS: si sta sperando che l’agente abbia pensato a tutto.

Per chiudere, tre controlli da fare lunedì su qualunque progetto Supabase che avete in mano: l’elenco delle tabelle in public con RLS disattivato, la ricerca di sb_secret_ o service_role nel JavaScript servito e nel repository, e le viste create senza security_invoker. Dieci minuti, che qualcuno con uno scanner sta già spendendo al posto vostro.

Fonti