[{"data":1,"prerenderedAt":425},["ShallowReactive",2],{"blog-it-supabase-rls-on-but-table-still-public":3},{"id":4,"title":5,"body":6,"category":387,"cover":388,"coverAlt":388,"description":389,"draft":390,"extension":391,"faq":392,"image":405,"keywords":406,"meta":412,"navigation":413,"ogTitle":414,"path":415,"published":416,"seo":417,"stem":418,"tldr":419,"updated":423,"__hash__":424},"blog_it\u002Fblog\u002Fsupabase-rls-on-but-table-still-public.md","Supabase Row Level Security è attivo. La tabella resta pubblica.",{"type":7,"value":8,"toc":376},"minimark",[9,13,21,26,29,32,35,39,42,45,56,77,80,89,93,104,195,201,204,212,216,223,230,233,239,249,253,256,259,298,301,306,310,313,316,327,330,334,363],[10,11,12],"p",{},"Hai attivato Row Level Security perché qualcosa te lo ha detto: l'advisor dentro\nSupabase, una checklist, il risultato di una scansione, qualcuno su un Discord.\nOra l'interruttore è verde nella tua dashboard. E ti stanno dicendo che la tua\ntabella è ancora leggibile dagli sconosciuti.",[10,14,15,16,20],{},"Ecco la parte che guida dopo guida racconta male: ",[17,18,19],"strong",{},"l'interruttore non protegge\nniente."," Decide che le tue regole vengano controllate. Le regole sono un'altra\ncosa, devi scriverle tu, e la regola più veloce da scrivere (quella che rimette\nin funzione un'app rotta) fa entrare tutti.",[22,23,25],"h2",{"id":24},"row-level-security-è-attivo-in-supabase-come-fa-la-tabella-a-restare-pubblica","Row Level Security è attivo in Supabase. Come fa la tabella a restare pubblica?",[10,27,28],{},"Perché attivarlo e decidere chi entra sono due passaggi diversi, e solo il primo\nè un interruttore.",[10,30,31],{},"Pensa all'interruttore come a mettere qualcuno alla porta. Azionarlo non decide\nchi passa. Decide che adesso qualcuno controlla un elenco. Le tue policy sono\nquell'elenco. Un elenco vuoto respinge tutti; un elenco che dice «tutti» non\nrespinge nessuno. Entrambe le cose sono Row Level Security attivo, e la tua\ndashboard mostra lo stesso verde in tutti e due i casi.",[10,33,34],{},"Per questo l'impostazione da sola risponde a molto poco, e per questo il nostro\nscanner non chiede mai a Supabase se è attiva. Lo chiede alla tabella. Manda la\nrichiesta che manderebbe uno sconosciuto, con la chiave pubblica che viaggia\ndentro la tua app, e guarda se torna una risposta. Chiede un conteggio invece\ndelle righe, così scopre che la porta si è aperta senza leggere nulla di quello\nche c'è dietro.",[22,36,38],{"id":37},"la-policy-che-ha-rimesso-in-piedi-la-tua-app-è-probabilmente-il-problema","La policy che ha rimesso in piedi la tua app è probabilmente il problema",[10,40,41],{},"Nel momento in cui attivi Row Level Security la tua app smette di mostrare dati,\ne quello che hai fatto subito dopo per rimetterla in funzione è proprio ciò che\nmerita uno sguardo.",[10,43,44],{},"Quella sequenza è del tutto normale, ed è lì che la cosa va storta. Con\nl'impostazione attiva e nessuna policy scritta, Postgres (il motore di database\nsu cui gira Supabase) rifiuta ogni richiesta per impostazione predefinita,\nquindi i tuoi elenchi tornano vuoti e le\ntue schermate restano bianche. Qualcosa deve finire sull'elenco. Se hai chiesto\na Cursor o a Lovable di sistemarla, o hai incollato il primo frammento che ha\nfatto sparire l'errore, quello che hai adesso somiglia probabilmente a questo:",[46,47,52],"pre",{"className":48,"code":50,"language":51},[49],"language-text","CREATE POLICY \"Enable read access for all users\"\n  ON public.profiles\n  FOR SELECT\n  USING (true);\n","text",[53,54,50],"code",{"__ignoreMap":55},"",[10,57,58,61,62,65,66,69,70,73,74,76],{},[53,59,60],{},"USING (true)"," è la condizione che una riga deve soddisfare prima che il\ndatabase la consegni. Ogni riga soddisfa ",[53,63,64],{},"true",". Lì dentro c'è un secondo\ndettaglio facile da superare senza vederlo: senza clausola ",[53,67,68],{},"TO",", una policy vale\nper ",[53,71,72],{},"public",", e ",[53,75,72],{}," comprende allo stesso modo i visitatori con accesso\nfatto e i perfetti sconosciuti.",[10,78,79],{},"Così l'app torna a funzionare, niente segnala un errore, e la tabella è\nesattamente leggibile come lo era prima che cominciassi.",[10,81,82,83,88],{},"Questo sistema la lettura, e solo la lettura. Se la tua app salva anche in\nquesta tabella, la cosa successiva che incontri è\n",[84,85,87],"a",{"href":86},"\u002Fblog\u002Fnew-row-violates-row-level-security-policy","new row violates row-level security policy",", cioè la stessa impostazione\nche rifiuta una scrittura, e nessuna policy di lettura la toglie.",[22,90,92],{"id":91},"i-quattro-stati-in-cui-può-trovarsi-una-tabella","I quattro stati in cui può trovarsi una tabella",[10,94,95,96,99,100,103],{},"Due sono sicuri e due no, e l'interruttore non ti dice quali. «Chiave\npubblicabile» qui sotto è quella che sta bene nella tua app: ",[53,97,98],{},"sb_publishable_…","\nnei progetti Supabase nuovi, ",[53,101,102],{},"anon"," in quelli più vecchi.",[105,106,107,126],"table",{},[108,109,110],"thead",{},[111,112,113,117,120,123],"tr",{},[114,115,116],"th",{},"Row Level Security",[114,118,119],{},"La policy",[114,121,122],{},"La tua app",[114,124,125],{},"Uno sconosciuto con la tua chiave pubblicabile di Supabase",[127,128,129,148,165,179],"tbody",{},[111,130,131,135,138,141],{},[132,133,134],"td",{},"Spento",[132,136,137],{},"indifferente, nessuna letta",[132,139,140],{},"Funziona",[132,142,143],{},[144,145,147],"key-verdict",{"type":146},"danger","Legge ogni riga",[111,149,150,153,156,159],{},[132,151,152],{},"Acceso",[132,154,155],{},"nessuna scritta",[132,157,158],{},"Rotta",[132,160,161],{},[144,162,164],{"type":163},"safe","Non legge nulla",[111,166,167,169,173,175],{},[132,168,152],{},[132,170,171],{},[53,172,60],{},[132,174,140],{},[132,176,177],{},[144,178,147],{"type":146},[111,180,181,183,188,190],{},[132,182,152],{},[132,184,185],{},[53,186,187],{},"USING (auth.uid() = user_id)",[132,189,140],{},[132,191,192],{},[144,193,194],{"type":163},"Legge solo le proprie",[196,197],"diagram",{"alt":198,"caption":199,"src":200},"Quattro tabelle una accanto all'altra. Nella prima l'interruttore è spento e tutte e sei le righe sono leggibili. Nella seconda l'interruttore è acceso, il riquadro della policy è vuoto e nessuna riga è leggibile. Nella terza l'interruttore è acceso con una policy che dice true, e tutte e sei le righe tornano leggibili. Nella quarta l'interruttore è acceso con una policy che confronta il proprietario della riga, e solo due righe sono leggibili.","Il segno sotto ogni colonna non segue l'interruttore sopra. Due di questi sono accesi e uno dei due consegna tutto.","\u002Fblog\u002Fsupabase-rls-on-but-table-still-public\u002Frls-four-states-1600x760.png",[10,202,203],{},"Le due colonne centrali sono la coppia su cui la gente inciampa. Stessa\nimpostazione, stessa pastiglia verde, esiti opposti, e la differenza è una\nparola dentro una regola che quasi nessuno apre.",[10,205,206,207,211],{},"Se preferisci non leggere ogni policy di persona, la nostra scansione gratuita\nfa al tuo database dal vivo la stessa domanda di uno sconosciuto e ti dice quali\ntabelle hanno risposto. Impiega una ventina di secondi e non serve un account:\n",[84,208,210],{"href":209},"\u002F#scan","scansiona la tua app",".",[22,213,215],{"id":214},"authenticated-non-vuol-dire-tuo","«Authenticated» non vuol dire «tuo»",[10,217,218,219,222],{},"Una policy che consente ",[53,220,221],{},"authenticated"," consente chiunque abbia un account,\nil che (se la tua app ha un modulo di iscrizione aperto) vuol dire chiunque\nsia disposto a compilarlo.",[10,224,225,226,229],{},"Questa è la versione più sottile dello stesso errore, e sopravvive a parecchie\nrevisioni perché sembra accurata. ",[53,227,228],{},"TO authenticated USING (true)"," si legge come\nuna restrizione, e lo è: esclude chi non si è mai iscritto. Quello che non fa è\nimpedire a un tuo cliente di leggere le righe di un altro cliente, che di solito\nè ciò che intendevi con «privato».",[10,231,232],{},"La regola che lo fa nomina il proprietario della riga:",[46,234,237],{"className":235,"code":236,"language":51},[49],"CREATE POLICY \"Users read their own rows\"\n  ON public.orders\n  FOR SELECT\n  TO authenticated\n  USING (auth.uid() = user_id);\n",[53,238,236],{"__ignoreMap":55},[10,240,241,244,245,248],{},[53,242,243],{},"auth.uid()"," è chi sta chiedendo. ",[53,246,247],{},"user_id"," è la colonna sulla riga che dice a\nchi appartiene. La riga torna quando le due cose coincidono, e resta dov'è\nquando non coincidono.",[22,250,252],{"id":251},"come-controllare-le-tue-tabelle-in-due-minuti","Come controllare le tue tabelle in due minuti",[10,254,255],{},"Apri Supabase, vai su Authentication → Policies, e leggi l'espressione dentro\nogni policy invece della pastiglia accanto a ogni tabella.",[10,257,258],{},"Tre cose da cercare:",[260,261,262,271,279],"ul",{},[263,264,265,270],"li",{},[17,266,267,268,211],{},"Una policy la cui condizione è ",[53,269,64],{}," Decidi, tabella per tabella, se ti\nstarebbe bene avere quei dati su una pagina aperta. Per un elenco di articoli\npubblicati, sì. Per qualsiasi cosa contenga una persona, no.",[263,272,273,278],{},[17,274,275,276,211],{},"Una policy senza clausola ",[53,277,68],{}," Vale per tutti, con accesso fatto o meno,\nanche quando il resto della regola sembra molto specifico.",[263,280,281,284,285,288,289,292,293,297],{},[17,282,283],{},"Una tabella con l'impostazione attiva, nessuna policy, e un'app che\ncomunque funziona."," Quella combinazione significa che qualcosa arriva ai tuoi\ndati per un'altra strada, e la spiegazione consueta è una chiave segreta\n(",[53,286,287],{},"sb_secret_…",", o ",[53,290,291],{},"service_role"," in un progetto più vecchio), che ignora ogni\npolicy tu abbia scritto.\n",[84,294,296],{"href":295},"\u002Fblog\u002Fwhich-api-keys-are-safe-in-your-frontend","Quali chiavi API sono sicure nel frontend","\nspiega come distinguerla da quella sicura.",[10,299,300],{},"L'altro controllo a cui si ricorre è aprire l'app in una finestra privata senza\naccedere. Vale la pena farlo, ma è bene sapere cosa dimostra. La tua app decide\ncosa disegnare. Il tuo database decide cosa consegnare. Sono decisioni diverse,\ne uno sconosciuto che salta le tue schermate ottiene la seconda.",[196,302],{"alt":303,"caption":304,"src":305},"Due percorsi verso le stesse righe. Passando dalle schermate dell'app tornano due righe su sei. Andando diritti all'indirizzo del database tornano tutte e sei.","Le schermate della tua app non sono la recinzione. La stessa tabella può mostrare due righe attraverso la tua app e consegnarne sei a una richiesta che non l'ha mai aperta.","\u002Fblog\u002Fsupabase-rls-on-but-table-still-public\u002Fapp-view-vs-database-1600x620.png",[22,307,309],{"id":308},"che-aspetto-ha-mentre-ti-sta-succedendo","Che aspetto ha mentre ti sta succedendo",[10,311,312],{},"Nessuno. È questa la forma di questo problema, ed è il motivo per cui resta lì\nper mesi.",[10,314,315],{},"Nel tuo builder non compare nessun errore. Niente rallenta, nessuna schermata si\nrompe, non arriva nessuna email. La tua app si comporta esattamente come il\ngiorno in cui l'hai lanciata, perché dal suo lato non è cambiato nulla: poteva\nsempre leggere quelle righe. Quello che è cambiato è che possono leggerle anche\ntutti gli altri, con la chiave che viaggia nel codice che il tuo sito manda a\nogni visitatore.",[10,317,318,319,322,323,326],{},"Quando emerge, emerge di lato. Una cliente chiede come faceva qualcuno a sapere\nuna cosa che sapeva solo la tua app. Un elenco di indirizzi che non hai mai\npubblicato salta fuori da qualche parte. E se la regola generosa copre anche la\nscrittura, ",[53,320,321],{},"FOR ALL"," invece di ",[53,324,325],{},"FOR SELECT",", allora chiunque può anche\nmodificare e cancellare righe: è la versione che si scopre come una tabella\nimprovvisamente vuota.",[10,328,329],{},"Anche le regole scivolano. Una migrazione, un cambio di schema, un'altra\nriparazione notturna che aveva bisogno che i dati si caricassero: ognuna di\nqueste può allentare una policy senza dirlo. Per questo qui conviene un secondo\nsguardo più avanti e non uno solo adesso. Tenerlo d'occhio è parte di quello che\nfa Reeve Care, anche se un promemoria nel calendario svolge lo stesso compito.",[22,331,333],{"id":332},"cosa-fare-questa-settimana","Cosa fare questa settimana",[335,336,337],"key-takeaways",{},[260,338,339,342,345,354,360],{},[263,340,341],{},"Apri Authentication → Policies in Supabase e leggi la condizione di ogni policy, tabella per tabella. La pastiglia sulla tabella non è la risposta.",[263,343,344],{},"Per ogni tabella che contiene persone, utenti, profili, ordini, messaggi, controlla che la regola nomini il proprietario della riga invece di consentire a tutti.",[263,346,347,348,350,351,353],{},"Sostituisci ogni ",[53,349,60],{}," su quelle tabelle con una regola che confronta ",[53,352,243],{}," con la colonna del proprietario, e verifica poi che la tua app si carichi ancora.",[263,355,356,357,359],{},"Aggiungi la clausola ",[53,358,68],{}," che avevi in mente. Una policy che ne è priva vale per gli sconosciuti quanto per i visitatori con accesso fatto.",[263,361,362],{},"Se una tabella ha l'impostazione attiva, nessuna policy, e la tua app mostra comunque i suoi dati, scopri cosa la sta aggirando prima di toccare qualsiasi altra cosa.",[10,364,365,366,370,371,375],{},"Comincia dalla tabella che ti metterebbe più in imbarazzo se fosse una pagina\naperta, e sistema quella oggi. La\n",[84,367,369],{"href":368},"\u002Fchecklist","checklist di sicurezza in 10 minuti"," copre questo insieme al resto\ndi ciò che vale la pena guardare in un'app appena lanciata, e la\n",[84,372,374],{"href":373},"\u002Fis-your-supabase-app-safe","guida alla sicurezza di Supabase"," ripassa cos'altro\nresta comunemente aperto.",{"title":55,"searchDepth":377,"depth":377,"links":378},3,[379,381,382,383,384,385,386],{"id":24,"depth":380,"text":25},2,{"id":37,"depth":380,"text":38},{"id":91,"depth":380,"text":92},{"id":214,"depth":380,"text":215},{"id":251,"depth":380,"text":252},{"id":308,"depth":380,"text":309},{"id":332,"depth":380,"text":333},"Basi della sicurezza",null,"Attivare Supabase Row Level Security non protegge una tabella. Lo fanno le policy, e quella che ha rimesso in piedi la tua app può far entrare chiunque.",false,"md",[393,396,399,402],{"q":394,"a":395},"Ho attivato Row Level Security e la mia app non mostra più dati. Ho rotto qualcosa?","No, è l'impostazione che sta facendo il suo lavoro. Con Row Level Security attivo e nessuna policy scritta, Postgres (il motore di database sotto Supabase) rifiuta ogni richiesta per impostazione predefinita, comprese quelle della tua stessa app. La soluzione è aggiungere una policy che descriva chi deve vedere quali righe. L'errore da evitare è aggiungerne una che consente a tutti, perché è quella che fa tornare a funzionare l'app e lascia la tabella aperta.",{"q":397,"a":398},"USING (true) è mai la policy giusta?","Sì, per dati davvero pubblici. Una tabella di articoli pubblicati, un catalogo prodotti, un elenco di locali su una mappa: sono fatti per essere letti da chiunque, e una policy che lo consente è corretta. Smette di esserlo nel momento in cui la tabella contiene persone. Chiediti se ti troveresti a tuo agio pubblicando il contenuto di quella tabella su una pagina aperta, e lascia che sia la risposta a decidere la policy.",{"q":400,"a":401},"Row Level Security mi protegge se la mia chiave segreta è trapelata?","No. Una chiave segreta di Supabase (sb_secret_ nei progetti nuovi, service_role in quelli più vecchi) aggira completamente Row Level Security, è il motivo per cui esiste. Ogni policy che hai scritto viene saltata, su ogni tabella. Se quella chiave è nel tuo frontend, le tue policy non stanno facendo nulla per te, e ruotarla è il primo lavoro, prima di qualunque intervento sulle regole.",{"q":403,"a":404},"Mi serve Row Level Security se la mia app ha già una schermata di accesso?","Sì. La schermata di accesso governa la tua app, e la tua app non è l'unica strada per arrivare al tuo database. Supabase dà a ogni progetto un indirizzo web che risponde direttamente alle richieste, e la chiave per parlarci sta nel codice che la tua app invia a ogni visitatore. Le policy sono la parte che vale a prescindere dalla porta da cui è arrivata la richiesta.","\u002Fblog\u002Fsupabase-rls-on-but-table-still-public\u002Fcard-800x500.png",[407,408,409,410,411],"supabase rls","row level security policy","tabella supabase pubblica","rls non funziona","sicurezza supabase",{},true,"Supabase RLS è attivo. La tua tabella è ancora pubblica.","\u002Fblog\u002Fsupabase-rls-on-but-table-still-public","2026-08-10",{"title":5,"description":389},"blog\u002Fsupabase-rls-on-but-table-still-public",[420,421,422],"In Supabase l'interruttore e le regole sono due cose diverse. Attivo senza regola non fa passare nessuno; attivo con la regola sbagliata non ferma nessuno.","La regola che rimette in funzione un'app rotta è di solito quella che consente qualunque richiesta, da chiunque.","Leggi la policy, non il pulsante. Una sola parola decide se uno sconosciuto può leggere la tua tabella.","2026-08-12","OlWe_n-E8ON-T57R7RAKCW5Hn3W8RR5qJQgJfX9Q-7k",1787826051029]