Vai al contenuto

La tua app Supabase è sicura?

Supabase può custodire dati veri degli utenti? Sì, ed è fatto per essere interrogato dal browser. Proprio per questo due impostazioni decidono tutto.

Vlad Tkachenko5 min di lettura
La scheda di controllo sicurezza di Reeve per le app Supabase, con il logo Supabase su una piastrella bianca.

I loghi sono di proprietà dei rispettivi titolari e sono mostrati solo per indicare la compatibilità.

In breve

  • Supabase può custodire dati veri degli utenti ed è fatto per essere interrogato direttamente da un browser. È esattamente per questo che le regole sulle tue tabelle fanno tutto il lavoro.
  • La chiave pubblicabile sta bene nella tua app. sb_secret_… e service_role aggirano ogni regola tu abbia scritto.
  • Le tabelle create nel Table Editor ricevono la Row Level Security in automatico. Quelle create eseguendo SQL no.

Supabase ti dà un vero database Postgres con un'API già davanti, ed è per questo che un'app può parlare con dati reali venti minuti dopo che hai iniziato. Ciò che sorprende le persone più tardi è che quella stessa API è raggiungibile da chiunque, da ovunque, con credenziali stampate dentro la tua app.

Ecco il punto che guida dopo guida racconta male: non è un difetto, e nasconderlo non è la soluzione. Supabase è progettato per essere interrogato direttamente da un browser. La protezione è sempre stata pensata per vivere altrove, e sapere dove è quasi tutto ciò che c'è da sapere per far girare un'app Supabase in tranquillità.

Perché il tuo database sta su internet di proposito

Perché la tua app ci parla dal browser del tuo visitatore, non da un server che gestisci tu.

Pensala come un negozio. La tua app è la vetrina, consegnata per intero a ogni visitatore. Il tuo database è il magazzino, e con Supabase il magazzino ha deliberatamente una porta propria sulla strada, così che la tua vetrina possa rifornirsi senza un servizio di consegna in mezzo. È quella porta a risparmiarti la scrittura di un backend.

Il che significa che la porta è pubblica, e il suo indirizzo è nella tua app perché la tua app deve bussarci. Non esiste un assetto in cui quell'indirizzo resti segreto. Quindi la domanda non è mai stata «degli estranei possono raggiungere il mio database». Possono, per costruzione. La domanda è che cosa consegna loro quando chiedono.

La chiave Supabase nella mia app dovrebbe starci?

Una delle due sì. L'altra mai, e si somigliano quasi in tutto.

Si chiamano entrambe chiavi API. Solo una delle due è mai stata pensata per essere letta.

La chiave pubblicabile (sb_publishable_… nei progetti nuovi, anon in quelli più vecchi) dice a quale progetto appartiene una richiesta. Non porta privilegi propri, ed è per questo che la documentazione di Supabase la descrive come sicura da includere in pagine web e app mobili. Trovarla nella tua app non è un problema.

La chiave segreta, sb_secret_… o la più vecchia service_role, è l'opposto. Aggira del tutto le tue regole e legge e scrive ogni riga di ogni tabella, da ovunque. Il suo posto è un server, una edge function, un worker: nessun luogo che un browser possa raggiungere.

Stanno una accanto all'altra sulla stessa pagina del pannello e hanno la stessa forma. Se una è mai finita incollata nel codice frontend, rigenerala nel pannello prima di modificare qualsiasi cosa, perché cancellare la riga non chiude la porta.

Quella pagina è Settings, poi API Keys, e contiene quattro valori invece di due.

L'impostazione che decide che cosa consegna la porta

La Row Level Security: un interruttore per tabella che decide, riga per riga, chi può leggere cosa. È l'intera ragione per cui una chiave pubblicabile è sicura.

La tua app e un estraneo bussano alla stessa porta. È l'impostazione a decidere chi entra, non le schermate della tua app.

Spenta, quella chiave restituisce l'intera tabella a chiunque la chieda. Accesa e con una policy scritta, è il database stesso a filtrare ogni richiesta, comprese le richieste che non sono mai passate dalla tua app.

Due dettagli decidono se ce l'hai davvero, e nessuno dei due è visibile dalla tua app:

Come è stata creata la tabella. Supabase attiva la Row Level Security in automatico per le tabelle fatte nel Table Editor del pannello. Le tabelle create eseguendo SQL non la ricevono e vanno attivate deliberatamente. File di migrazione, script e schemi generati dall'IA creano tutti le tabelle eseguendo SQL, quindi un progetto costruito così può avere una fila di tabelle non protette che sembrano esattamente uguali a quelle protette.

Che cosa dice la policy. Attiva vuol dire chiusa finché una policy non la apre. Una policy che permette a tutti la spalanca mentre il pannello continua a riportare la tabella come avente la Row Level Security attiva, lo stato che sembra protetto e non lo è.

Lo storage è ancora un'altra cosa. Un bucket pubblico è elencabile e scaricabile da chiunque abbia l'indirizzo del tuo progetto, qualunque cosa dicano le regole delle tue tabelle.

Come controllare il tuo progetto Supabase in una decina di minuti

Authentication → Policies. Leggi tutto l'elenco. Qualsiasi tabella che mostri la Row Level Security come disattivata è leggibile da chiunque. Per quelle che la mostrano attiva, apri la policy e leggi che cosa permette davvero.

Settings → API. Conferma che la chiave che la tua app spedisce sia quella pubblicabile. Se una chiave segreta è mai stata nel codice frontend, rigenerala qui per prima cosa. Se la tua dashboard mostra chiavi che iniziano con sb_publishable_ e sb_secret_ invece di anon e service_role, quello è il formato più recente, e la stessa regola decide quale delle due va nella tua app.

Storage → Buckets. Controlla quali sono pubblici e se è ciò che intendevi per i file al loro interno.

E poi guarda da fuori. Tutto quanto sopra è ciò che il tuo pannello dichiara configurato. Che cosa ottenga davvero una richiesta anonima è un'altra domanda, ed è quella a cui risponde la nostra scansione gratuita: interroga come farebbe un estraneo, senza leggere nessuna delle tue righe, e ti dà un voto in una ventina di secondi: controlla che cosa espone la tua app.

Cosa fare

  • La tua API Supabase è pubblica di proposito. La protezione sono le regole sulle tue tabelle, mai la segretezza dell'indirizzo.
  • La chiave pubblicabile sta bene nella tua app. sb_secret_… e service_role mai, e una trapelata ignora ogni regola tu abbia scritto.
  • Le tabelle fatte nel Table Editor ricevono la Row Level Security in automatico. Quelle create eseguendo SQL no.
  • Attiva non è limitata. Leggi la policy, non solo l'interruttore.
  • I bucket di storage hanno impostazioni proprie, quindi tabelle chiuse non dicono nulla dei tuoi file.

Apri Authentication → Policies e scorri l'elenco una volta. Le tabelle contrassegnate come disattivate sono quelle da cui partire, e se preferisci attraversare tutta la superficie come un elenco, la checklist di sicurezza in 10 minuti ne è la versione breve. Qualunque cosa tu trovi, sapere di poter rimettere i dati al loro posto conta quanto chiuderli a chiave, e dipende interamente da che cosa stai salvando.

Se preferisci che non dipenda dal tuo ricordartene, ecco come funzionano i backup automatici di Supabase.

Cosa può davvero andare storto con Supabase

Niente di tutto questo significa che hai fatto qualcosa di sbagliato: sono le solite falle quando vai di fretta. Ecco cosa vale la pena controllare:

  • Row Level Security (RLS) disattivata

    L'RLS è la regola di Supabase su chi può leggere o modificare ogni riga di una tabella. Con la chiave pubblica «anon» (che deve stare nella tua app), chiunque può interrogare il tuo database direttamente. L'RLS è ciò che gli impedisce di vedere righe che non sono sue. Se è disattivata, una tabella può essere del tutto leggibile, o persino modificabile, da chiunque. È l'impostazione più importante di Supabase, e Reeve la controlla contando le righe, mai leggendole.

  • Una chiave service_role trapelata

    Supabase ti dà due chiavi. La chiave «anon» è pubblica per progettazione e sicura nel browser. La chiave «service_role» aggira tutte le tue regole di sicurezza e deve vivere solo su un server. Se mai finisce nel codice del front-end della tua app, qualcuno può fare qualsiasi cosa con i tuoi dati. Reeve decodifica le chiavi che trova e ti dice esattamente quale è esposta: la «anon» sicura riceve un segno di spunta verde, la «service_role» è un allarme rosso.

  • Bucket di archiviazione pubblici

    I file che le persone caricano vivono in «bucket» di Supabase Storage. Un bucket impostato come pubblico significa che chiunque può elencare e scaricare ciò che c'è dentro, così i caricamenti privati possono diventare visibili a tutti. Reeve controlla se i tuoi bucket sono elencabili; non scarica mai i file di nessuno.

  • La tua API aperta a qualsiasi sito (CORS)

    Supabase dà al tuo database un indirizzo web (tramite PostgREST). Combinato con un'impostazione di condivisione troppo permissiva, un altro sito potrebbe chiamare i tuoi dati dal browser di un visitatore. Reeve controlla se i tuoi endpoint rispondono agli estranei e ad altri siti, senza mai usarli per cambiare nulla.

  • Tabelle ed endpoint esposti senza regole

    Ogni tabella che crei è raggiungibile tramite l'API di Supabase; è così per progettazione, e l'RLS dovrebbe proteggerla. Ma una nuova tabella aggiunta in fretta, prima che le sue regole siano impostate, può restare brevemente (o a lungo) aperta. Reeve controlla cosa è davvero raggiungibile dall'esterno.

  • Chiavi o configurazione lasciate nell'app pubblicata

    Oltre alle chiavi di Supabase stesso, le app spesso portano altre impostazioni e segreti nel loro codice front-end o in un .env esposto. Reeve legge il codice caricato della tua app e controlla se ci sono file di configurazione raggiungibili, poi ti dice quali valori sono sicuri da rendere pubblici e quali no.

Cos'è Reeve, e cosa non è

Reeve è un controllo gratuito, in sola lettura, dall'esterno, come un ispettore che prova le porte senza entrare. È veloce e coglie gli errori comuni ad alto impatto. Non è un audit di sicurezza completo, e un voto pulito non è una garanzia. Significa che le porte ovvie sono chiuse. Tutto ciò che Reeve fa è passivo: conta le righe invece di leggerle, e non scarica mai i tuoi file.

Supabase ti dà veri strumenti di sicurezza (un Security Advisor e un linter del database che segnalano i problemi di RLS e di esposizione proprio nella dashboard), e vale davvero la pena usarli. Cosa aggiunge Reeve: la maggior parte di chi costruisce su Supabase vive nel proprio creatore di app, non nell'editor SQL, e l'advisor parla da sviluppatori. Reeve controlla tutta la tua app dall'esterno, come un aggressore arriverebbe ai tuoi dati, e spiega ciò che trova con parole semplici. E se preferisci non tenerla d'occhio tu stesso, possiamo farlo noi.

Vuoi che sia gestito, non solo controllato?

Reeve Care continua a tenere d'occhio la tua app, fa il backup dei tuoi dati e ti aiuta a sistemare le cose quando si rompono, così puoi continuare a creare invece di preoccuparti.

Scopri Reeve Care

FAQ

Cos'è l'RLS di Supabase e ne ho davvero bisogno?

L'RLS (Row Level Security) decide chi può vedere o modificare ogni riga delle tue tabelle. Dato che la tua app invia una chiave pubblica «anon» capace di interrogare il database direttamente, l'RLS è ciò che impedisce che i dati di un utente siano visibili a tutti. Sì: per qualsiasi tabella con dati reali, ti serve attivo. Reeve controlla se lo è, senza leggere i tuoi dati.

La chiave anon di Supabase è sicura da esporre?

Sì. La chiave «anon» è progettata per vivere nel tuo front-end, e da sola fa solo ciò che le tue regole RLS permettono. La chiave che non deve mai essere esposta è la «service_role», che ignora tutte le regole. Reeve decodifica le chiavi della tua app e ti dice quale è quale.

Cosa succede se la mia chiave service_role trapela?

La chiave service_role aggira ogni regola di sicurezza, quindi chiunque la abbia può leggere, cambiare o cancellare tutti i tuoi dati. Se è nel tuo codice front-end, trattala come compromessa: ruotala nella dashboard di Supabase e spostala su un server. Reeve segnala una chiave service_role esposta come un problema critico.

Reeve può controllare il mio Supabase senza la password del mio database?

Sì. Reeve usa solo ciò che la tua app già espone pubblicamente: la stessa chiave anon e gli stessi endpoint che usa il browser di qualsiasi visitatore. Non ha mai bisogno della password del tuo database, non accede mai come amministratore e non legge né scarica mai le tue righe o i tuoi file.

Perché Supabase lascia che un browser parli con il mio database?

Perché è il prodotto. Supabase mette un'API davanti a Postgres così che la tua app possa interrogare i dati senza che tu scriva e ospiti un backend, ed è questo che rende così veloce costruirci sopra. La sicurezza non arriva dal nascondere quell'API, che non si può nascondere. Arriva da Postgres che rifiuta di restituire righe a cui il visitatore non ha diritto.

Attivare la Row Level Security su una tabella la rende privata?

La rende chiusa finché non scrivi una policy, che è il punto di partenza sicuro. Ma una policy che permette a tutti la riapre, mentre il pannello continua a mostrare la tabella con la Row Level Security attiva. Attiva e limitata sono due stati diversi, e solo uno dei due si vede a colpo d'occhio.

Quali tabelle rischiano di più di non essere protette?

Quelle create eseguendo SQL anziché cliccando nel Table Editor: file di migrazione, script e schemi scritti per te da un builder con IA. Il Table Editor attiva la Row Level Security in automatico. SQL no, e dopo nulla segnala la differenza.

Il mio storage Supabase è coperto dalla Row Level Security?

Lo storage ha impostazioni proprie e separate, quindi un database ben chiuso non ti dice nulla dei tuoi file. Un bucket contrassegnato come pubblico può essere elencato e scaricato da chiunque conosca l'indirizzo del tuo progetto, che la tua app rimandi o no a ciò che contiene.

Scritto da

Vlad Tkachenko

Fondatore di Reeve

Passo le giornate a guardare app costruite con Lovable, Bolt, v0, Cursor e Replit, e la breve lista di errori che vi ricompaiono di continuo.

Altro sull'autore

Hai creato la tua app in uno strumento specifico? Ecco lo stesso resoconto onesto per:

← Vedi tutte le guide sulla sicurezza dei creatori

Controllo esterno automatizzato, non un audit completo. L'assenza di risultati non è una garanzia di sicurezza.