[{"data":1,"prerenderedAt":427},["ShallowReactive",2],{"blog-de-supabase-rls-on-but-table-still-public":3},{"id":4,"title":5,"body":6,"category":389,"cover":390,"coverAlt":390,"description":391,"draft":392,"extension":393,"faq":394,"image":407,"keywords":408,"meta":414,"navigation":415,"ogTitle":416,"path":417,"published":418,"seo":419,"stem":420,"tldr":421,"updated":425,"__hash__":426},"blog_de\u002Fblog\u002Fsupabase-rls-on-but-table-still-public.md","Supabase Row Level Security ist an. Die Tabelle ist trotzdem offen.",{"type":7,"value":8,"toc":378},"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,300,303,308,312,315,318,329,332,336,365],[10,11,12],"p",{},"Du hast Row Level Security angeschaltet, weil irgendetwas dir dazu geraten hat:\nder Advisor in Supabase, eine Checkliste, ein Scan-Ergebnis, jemand in einem\nDiscord. Der Schalter ist jetzt grün in deinem Dashboard. Und trotzdem hörst du,\ndeine Tabelle sei weiterhin für Fremde lesbar.",[10,14,15,16,20],{},"Hier ist der Punkt, den Anleitung um Anleitung falsch darstellt: ",[17,18,19],"strong",{},"Der Schalter\nschützt gar nichts."," Er entscheidet nur, dass deine Regeln überhaupt geprüft\nwerden. Die Regeln sind eine eigene Sache, du musst sie selbst schreiben, und\ndie Regel, die am schnellsten geschrieben ist (die, mit der eine kaputte App\nwieder läuft), lässt alle herein.",[22,23,25],"h2",{"id":24},"row-level-security-ist-in-supabase-an-wieso-ist-die-tabelle-noch-offen","Row Level Security ist in Supabase an. Wieso ist die Tabelle noch offen?",[10,27,28],{},"Weil Anschalten und Festlegen, wer hereindarf, zwei verschiedene Schritte sind\nund nur der erste ein Schalter ist.",[10,30,31],{},"Stell dir den Schalter als jemanden vor, der an der Tür steht. Ihn umzulegen\nentscheidet nicht, wer hindurchdarf. Es entscheidet, dass jetzt jemand eine\nListe abgleicht. Deine Policies sind diese Liste. Eine leere Liste weist alle\nab, eine Liste mit „alle\" weist niemanden ab. Beides ist Row Level Security im\nangeschalteten Zustand, und dein Dashboard zeigt in beiden Fällen dasselbe Grün.",[10,33,34],{},"Deshalb beantwortet die Einstellung für sich genommen sehr wenig, und deshalb\nfragt unser Scanner Supabase nie, ob sie aktiviert ist. Er fragt stattdessen die\nTabelle. Er schickt die Anfrage, die auch ein Fremder schicken würde, mit dem\nöffentlichen Schlüssel aus deiner App, und sieht nach, ob eine Antwort\nzurückkommt. Er fragt nach einer Anzahl statt nach den Zeilen und erfährt so,\ndass die Tür aufging, ohne zu lesen, was dahinterliegt.",[22,36,38],{"id":37},"die-policy-die-deine-app-repariert-hat-ist-wahrscheinlich-das-problem","Die Policy, die deine App repariert hat, ist wahrscheinlich das Problem",[10,40,41],{},"In dem Moment, in dem du Row Level Security anschaltest, zeigt deine App keine\nDaten mehr, und was du danach getan hast, um sie wieder zum Laufen zu bringen,\nist genau die Stelle, auf die es sich zu schauen lohnt.",[10,43,44],{},"Dieser Ablauf ist völlig normal, und hier geht es schief. Mit der Einstellung an\nund ohne geschriebene Policies weist Postgres (die Datenbank-Engine, auf der\nSupabase läuft) standardmäßig jede Anfrage ab, deine Listen kommen leer zurück und deine Bildschirme bleiben blank.\nIrgendetwas muss auf die Liste. Wenn du Cursor oder Lovable gebeten hast, das zu\nreparieren, oder den ersten Schnipsel eingefügt hast, der die Fehlermeldung\nbeendet hat, sieht das Ergebnis vermutlich so aus:",[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)"," ist die Bedingung, die eine Zeile erfüllen muss, bevor die\nDatenbank sie herausgibt. Jede Zeile erfüllt ",[53,63,64],{},"true",". Darin steckt ein zweites\nDetail, an dem man leicht vorbeigeht: Ohne ",[53,67,68],{},"TO","-Klausel gilt eine Policy für\n",[53,71,72],{},"public",", und ",[53,75,72],{}," umfasst angemeldete Besucher und völlig Fremde\ngleichermaßen.",[10,78,79],{},"Die App läuft also wieder, nichts zeigt einen Fehler, und die Tabelle ist genau\nso lesbar wie vorher.",[10,81,82,83,88],{},"Das repariert das Lesen, und nur das Lesen. Wenn deine App in diese Tabelle auch\nspeichert, ist das Nächste, dem du begegnest,\n",[84,85,87],"a",{"href":86},"\u002Fblog\u002Fnew-row-violates-row-level-security-policy","new row violates row-level security policy",", also dieselbe Einstellung,\ndie einen Schreibvorgang ablehnt, und keine Lese-Policy räumt das weg.",[22,90,92],{"id":91},"die-vier-zustände-in-denen-eine-tabelle-sein-kann","Die vier Zustände, in denen eine Tabelle sein kann",[10,94,95,96,99,100,103],{},"Zwei davon sind sicher und zwei nicht, und der Schalter sagt dir nicht, welche\nwelche sind. „Öffentlicher Schlüssel\" unten meint den, der in deine App gehört:\n",[53,97,98],{},"sb_publishable_…"," in neueren Supabase-Projekten, ",[53,101,102],{},"anon"," in älteren.",[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],{},"Die Policy",[114,121,122],{},"Deine App",[114,124,125],{},"Ein Fremder mit deinem öffentlichen Supabase-Schlüssel",[127,128,129,148,165,179],"tbody",{},[111,130,131,135,138,141],{},[132,133,134],"td",{},"Aus",[132,136,137],{},"egal, keine wird geprüft",[132,139,140],{},"Läuft",[132,142,143],{},[144,145,147],"key-verdict",{"type":146},"danger","Liest jede Zeile",[111,149,150,153,156,159],{},[132,151,152],{},"An",[132,154,155],{},"keine geschrieben",[132,157,158],{},"Kaputt",[132,160,161],{},[144,162,164],{"type":163},"safe","Liest nichts",[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},"Liest nur die eigenen",[196,197],"diagram",{"alt":198,"caption":199,"src":200},"Vier Tabellen nebeneinander. Bei der ersten ist der Schalter aus und alle sechs Zeilen sind lesbar. Bei der zweiten ist der Schalter an, das Policy-Feld leer, und keine Zeile ist lesbar. Bei der dritten ist der Schalter an und die Policy lautet true, und alle sechs Zeilen sind wieder lesbar. Bei der vierten ist der Schalter an und die Policy vergleicht den Besitzer der Zeile, und nur zwei Zeilen sind lesbar.","Das Häkchen unter jeder Spalte folgt nicht dem Schalter darüber. Zwei davon sind angeschaltet, und eines davon gibt alles heraus.","\u002Fblog\u002Fsupabase-rls-on-but-table-still-public\u002Frls-four-states-1600x760.png",[10,202,203],{},"Die beiden mittleren Spalten sind das Paar, an dem Menschen hängen bleiben.\nDieselbe Einstellung, dasselbe grüne Abzeichen, entgegengesetzte Ergebnisse,\nund der Unterschied ist ein Wort in einer Regel, die kaum jemand je öffnet.",[10,205,206,207,211],{},"Wenn du nicht jede Policy selbst lesen möchtest: Unser kostenloser Scan stellt\ndeiner Datenbank live dieselbe Frage wie ein Fremder und sagt dir, welche\nTabellen geantwortet haben. Er dauert rund 20 Sekunden und braucht kein Konto:\n",[84,208,210],{"href":209},"\u002F#scan","App scannen",".",[22,213,215],{"id":214},"authenticated-bedeutet-nicht-dir-gehörend","„Authenticated\" bedeutet nicht „dir gehörend\"",[10,217,218,219,222],{},"Eine Policy, die ",[53,220,221],{},"authenticated"," erlaubt, erlaubt alle, die ein Konto haben,\nund wenn deine App ein offenes Anmeldeformular hat, sind das alle, die bereit\nsind, es auszufüllen.",[10,224,225,226,229],{},"Das ist die feinere Variante desselben Fehlers, und sie übersteht so manche\nDurchsicht, weil sie sorgfältig aussieht. ",[53,227,228],{},"TO authenticated USING (true)"," liest\nsich wie eine Einschränkung, und das ist es auch: Es schließt aus, wer sich nie\nangemeldet hat. Was es nicht tut, ist einen deiner Kunden davon abzuhalten, die\nZeilen eines anderen Kunden zu lesen, und genau das meintest du vermutlich mit\n„privat\".",[10,231,232],{},"Die Regel, die das leistet, benennt, wem die Zeile gehört:",[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()"," ist, wer gerade fragt. ",[53,246,247],{},"user_id"," ist die Spalte auf der Zeile, die\nsagt, wem sie gehört. Die Zeile kommt zurück, wenn beides übereinstimmt, und\nbleibt liegen, wenn nicht.",[22,250,252],{"id":251},"so-prüfst-du-deine-eigenen-tabellen-in-zwei-minuten","So prüfst du deine eigenen Tabellen in zwei Minuten",[10,254,255],{},"Öffne Supabase, geh auf Authentication → Policies und lies den Ausdruck in jeder\nPolicy statt das Abzeichen neben jeder Tabelle.",[10,257,258],{},"Drei Dinge, auf die du achten solltest:",[260,261,262,272,281],"ul",{},[263,264,265,271],"li",{},[17,266,267,268,270],{},"Eine Policy, deren Bedingung ",[53,269,64],{}," ist."," Entscheide Tabelle für Tabelle,\nob es dir recht wäre, diese Daten auf einer öffentlichen Seite stehen zu\nhaben. Bei einer Liste veröffentlichter Artikel: ja. Bei allem, in dem ein\nMensch vorkommt: nein.",[263,273,274,280],{},[17,275,276,277,279],{},"Eine Policy ohne ",[53,278,68],{},"-Klausel."," Sie gilt für alle, angemeldet oder nicht,\nauch wenn der Rest der Regel sehr konkret aussieht.",[263,282,283,286,287,290,291,294,295,299],{},[17,284,285],{},"Eine Tabelle mit angeschalteter Einstellung, ganz ohne Policies, und einer\nApp, die trotzdem läuft."," Diese Kombination heißt, dass etwas anderes an\ndeine Daten kommt, und die übliche Erklärung ist ein geheimer Schlüssel\n(",[53,288,289],{},"sb_secret_…",", oder ",[53,292,293],{},"service_role"," in einem älteren Projekt), der jede von dir\ngeschriebene Policy ignoriert.\n",[84,296,298],{"href":297},"\u002Fblog\u002Fwhich-api-keys-are-safe-in-your-frontend","Welche API-Schlüssel im Frontend sicher sind","\nzeigt, wie du ihn vom sicheren unterscheidest.",[10,301,302],{},"Die andere Prüfung, zu der viele greifen, ist, die App im privaten Fenster ohne\nAnmeldung zu öffnen. Sie lohnt sich, aber wisse, was sie beweist. Deine App\nentscheidet, was sie anzeigt. Deine Datenbank entscheidet, was sie herausgibt.\nDas sind zwei verschiedene Entscheidungen, und ein Fremder, der deine\nBildschirme überspringt, bekommt die zweite.",[196,304],{"alt":305,"caption":306,"src":307},"Zwei Wege zu denselben Zeilen. Über die Bildschirme der App kommen zwei von sechs Zeilen zurück. Direkt an die Adresse der Datenbank kommen alle sechs zurück.","Die Bildschirme deiner App sind nicht der Zaun. Dieselbe Tabelle kann über deine App zwei Zeilen zeigen und einer Anfrage, die sie nie geöffnet hat, alle sechs herausgeben.","\u002Fblog\u002Fsupabase-rls-on-but-table-still-public\u002Fapp-view-vs-database-1600x620.png",[22,309,311],{"id":310},"wie-es-aussieht-wenn-dir-das-gerade-passiert","Wie es aussieht, wenn dir das gerade passiert",[10,313,314],{},"Nach nichts. Das ist die Form dieser Sache, und deshalb bleibt sie monatelang\nliegen.",[10,316,317],{},"Es erscheint kein Fehler in deinem Builder. Nichts wird langsamer, kein\nBildschirm bricht, keine E-Mail kommt. Deine App verhält sich exakt wie am Tag\ndes Starts, denn von ihrer Seite hat sich nichts geändert: Sie durfte diese\nZeilen immer lesen. Geändert hat sich, dass alle anderen sie ebenfalls lesen\ndürfen, mit dem Schlüssel, der in dem Code steckt, den deine Seite an jeden\nBesucher ausliefert.",[10,319,320,321,324,325,328],{},"Wenn es auffällt, dann seitlich. Eine Kundin fragt, woher jemand etwas wusste,\ndas nur deine App wusste. Eine Adressliste, die du nie veröffentlicht hast,\ntaucht irgendwo auf. Und wenn die großzügige Regel auch das Schreiben abdeckt\n(",[53,322,323],{},"FOR ALL"," statt ",[53,326,327],{},"FOR SELECT","), dann können Zeilen auch von allen geändert und\ngelöscht werden. Diese Variante entdecken Leute als eine Tabelle, die plötzlich\nleer ist.",[10,330,331],{},"Regeln verrutschen außerdem. Eine Migration, eine Schemaänderung, noch eine\nnächtliche Reparatur, die Daten laden musste: Jede davon kann eine Policy\nlockern, ohne es zu sagen. Deshalb lohnt sich hier ein zweiter Blick später und\nnicht nur einer jetzt. Darauf zu achten ist Teil dessen, was Reeve Care tut.\nEine Erinnerung im Kalender leistet dasselbe.",[22,333,335],{"id":334},"was-du-diese-woche-tun-solltest","Was du diese Woche tun solltest",[337,338,339],"key-takeaways",{},[260,340,341,344,347,356,362],{},[263,342,343],{},"Öffne in Supabase Authentication → Policies und lies die Bedingung jeder Policy, Tabelle für Tabelle. Das Abzeichen an der Tabelle ist nicht die Antwort.",[263,345,346],{},"Prüfe bei jeder Tabelle mit Menschen darin (Nutzer, Profile, Bestellungen, Nachrichten), ob die Regel den Besitzer der Zeile benennt, statt alle zu erlauben.",[263,348,349,350,352,353,355],{},"Ersetze jedes ",[53,351,60],{}," auf diesen Tabellen durch eine Regel, die ",[53,354,243],{}," mit der Besitzerspalte vergleicht, und prüfe danach, ob deine App noch lädt.",[263,357,358,359,361],{},"Ergänze die ",[53,360,68],{},"-Klausel, die du gemeint hast. Eine Policy ohne sie gilt für Fremde genauso wie für angemeldete Besucher.",[263,363,364],{},"Wenn eine Tabelle die Einstellung an hat, keine Policies besitzt und deine App ihre Daten trotzdem zeigt, finde heraus, was sie umgeht, bevor du sonst etwas anfasst.",[10,366,367,368,372,373,377],{},"Fang mit der Tabelle an, die dir am peinlichsten wäre, wenn sie eine öffentliche\nSeite wäre, und bring die heute in Ordnung. Die\n",[84,369,371],{"href":370},"\u002Fchecklist","10-Minuten-Sicherheitscheckliste"," deckt das zusammen mit dem\nÜbrigen ab, was bei einer frisch gestarteten App einen Blick wert ist, und der\n",[84,374,376],{"href":375},"\u002Fis-your-supabase-app-safe","Supabase-Sicherheitsleitfaden"," geht durch, was\nsonst noch offen zu bleiben pflegt.",{"title":55,"searchDepth":379,"depth":379,"links":380},3,[381,383,384,385,386,387,388],{"id":24,"depth":382,"text":25},2,{"id":37,"depth":382,"text":38},{"id":91,"depth":382,"text":92},{"id":214,"depth":382,"text":215},{"id":251,"depth":382,"text":252},{"id":310,"depth":382,"text":311},{"id":334,"depth":382,"text":335},"Sicherheitsgrundlagen",null,"Supabase Row Level Security anzuschalten schützt keine Tabelle. Erst deine Policies tun das, und die, die deine App repariert hat, lässt oft alle herein.",false,"md",[395,398,401,404],{"q":396,"a":397},"Ich habe Row Level Security angeschaltet und meine App zeigt keine Daten mehr. Habe ich etwas kaputt gemacht?","Nein, so soll die Einstellung wirken. Mit Row Level Security an und ohne geschriebene Policies weist Postgres (die Datenbank-Engine unter Supabase) standardmäßig jede Anfrage ab, auch die deiner eigenen App. Die Lösung ist eine Policy, die beschreibt, wer welche Zeilen sehen darf. Der Fehler, den es zu vermeiden gilt: eine Policy, die alle erlaubt. Genau die bringt die App zurück und lässt die Tabelle offen.",{"q":399,"a":400},"Ist USING (true) jemals die richtige Policy?","Ja, für Daten, die wirklich öffentlich sind. Eine Tabelle mit veröffentlichten Blogbeiträgen, ein Produktkatalog, eine Liste von Orten auf einer Karte: Die sollen von allen gelesen werden können, und eine Policy, die genau das erlaubt, ist richtig. Sie hört in dem Moment auf richtig zu sein, in dem Menschen in der Tabelle stehen. Frag dich, ob du den Inhalt dieser Tabelle auf einer öffentlichen Seite stehen lassen würdest, und lass die Antwort die Policy entscheiden.",{"q":402,"a":403},"Schützt Row Level Security mich, wenn mein geheimer Schlüssel abgeflossen ist?","Nein. Ein geheimer Supabase-Schlüssel (sb_secret_ in neueren Projekten, service_role in älteren) umgeht Row Level Security vollständig, dafür ist er gedacht. Jede Policy, die du geschrieben hast, wird übersprungen, auf jeder Tabelle. Liegt dieser Schlüssel in deinem Frontend, tun deine Policies nichts für dich, und den Schlüssel zu rotieren ist die erste Aufgabe, vor jeder Arbeit an den Regeln.",{"q":405,"a":406},"Brauche ich Row Level Security, wenn meine App schon einen Login hat?","Ja. Dein Login steuert deine App, und deine App ist nicht der einzige Weg zu deiner Datenbank. Supabase gibt jedem Projekt eine Webadresse, die Anfragen direkt beantwortet, und der Schlüssel dafür steckt in dem Code, den deine App an jeden Besucher ausliefert. Policies sind der Teil, der unabhängig davon greift, durch welche Tür die Anfrage kam.","\u002Fblog\u002Fsupabase-rls-on-but-table-still-public\u002Fcard-800x500.png",[409,410,411,412,413],"supabase rls","row level security policy","supabase tabelle öffentlich","rls funktioniert nicht","supabase sicherheit",{},true,"Supabase RLS ist an. Deine Tabelle ist trotzdem öffentlich.","\u002Fblog\u002Fsupabase-rls-on-but-table-still-public","2026-08-10",{"title":5,"description":391},"blog\u002Fsupabase-rls-on-but-table-still-public",[422,423,424],"In Supabase sind der Schalter und die Regeln zwei getrennte Dinge. An ohne Regel sperrt alle aus, an mit der falschen Regel sperrt niemanden aus.","Die Regel, die eine kaputte App wieder zum Laufen bringt, erlaubt meistens jede Anfrage von jedem.","Lies die Policy, nicht den Schalter. Ein einziges Wort darin entscheidet, ob Fremde deine Tabelle lesen können.","2026-08-12","Roa5FDfPh91F_FQF3DeroD5tonFL1uYydK4eZGQETxc",1787826048205]