Wat is Row Level Security?
Supabase is een database met alles eromheen: inloggen, bestandsopslag en een kant-en-klare manier om de database vanuit je website aan te spreken. Dat laatste maakt het zo snel om mee te bouwen, en het is ook waarom Lovable, Bolt en andere AI-tools er graag mee werken. Je app praat rechtstreeks vanuit de browser met de database, zonder eigen server ertussen.
Maar als de browser rechtstreeks met de database praat, wie houdt dan in de gaten wat iemand mag opvragen? Dat doet Row Level Security. Zie je database als een archiefkast vol mappen. Zonder RLS mag iedereen die bij de kast komt elke map pakken. Met RLS staat er bij de kast iemand die bij elke map kijkt of jij die mag inzien: alleen je eigen facturen, alleen de projecten van je eigen team.
De regels die bepalen wie wat mag, heten policies. RLS is de schakelaar die zegt ‘controleer de regels’, de policies zijn de regels zelf. Je hebt ze allebei nodig.
Twee sleutels: de publieke en de geheime
Elk Supabase-project heeft twee soorten sleutels. In oudere projecten heten ze anon en service_role, in nieuwere projecten publishable en secret. Het verschil is groot.
De publieke sleutel staat in de code van je website. Iedereen die je site bezoekt, kan hem vinden, en dat is geen fout: zo is Supabase ontworpen. Met die sleutel kan iemand alleen doen wat je policies toestaan. Maar daar zit de valkuil. Staat RLS uit op een tabel, dan is er niets om toe te staan of te weigeren, en kan iedereen met de publieke sleutel die hele tabel opvragen. Bij een incident met apps die met Lovable waren gebouwd, dat in 2025 als CVE-2025-48757 werd geregistreerd, ging het precies om dit soort te ruime databaseregels.
De geheime sleutel omzeilt RLS volledig. Hij is bedoeld voor taken op een server, zoals een betaling verwerken of een beheerdersactie uitvoeren. De documentatie van Supabase is duidelijk: gebruik hem alleen op een server en zet hem nooit in de browser of op een andere plek waar gebruikers hem kunnen zien.
- Publieke sleutel (anon of publishable)
- Staat in je website en mag zichtbaar zijn. Kan alleen wat je policies toestaan, dus alleen veilig met RLS aan.
- Geheime sleutel (service_role of secret)
- Omzeilt alle policies. Hoort alleen op een server, nooit in de browser.
- Policy
- Een regel op een tabel die zegt wie welke rijen mag lezen, toevoegen, wijzigen of verwijderen.
Zo controleer je of RLS aanstaat
Je hoeft geen developer te zijn om te zien of RLS aanstaat. Het Supabase-dashboard laat het per tabel zien, en waarschuwt zelf voor de bekendste problemen. Loop deze stappen door:
- 1Log in op het dashboard van Supabase en open het project van je app.
- 2Open de Table Editor en loop alle tabellen langs. Tabellen zonder RLS krijgen in het dashboard een duidelijke waarschuwing.
- 3Open het overzicht met policies en kijk per tabel welke regels er zijn. Een tabel met RLS aan maar zonder policies is dicht voor de publieke sleutel. Een policy die alles voor iedereen toestaat, is net zo open als RLS uit.
- 4Open de Security Advisor. Die waarschuwt onder meer voor tabellen zonder RLS en voor views die RLS omzeilen.
- 5Vergeet de bestandsopslag (Storage) niet: kijk welke buckets openbaar zijn en welke policies er gelden.
Wat is een policy? Een voorbeeld in gewone taal
Stel, je app is een eenvoudige projectplanner. Elk project staat als rij in de tabel projects, met in een kolom wie de eigenaar is. Een goede policy voor lezen zegt in gewone taal: ‘een gebruiker mag een rij alleen zien als hij zelf de eigenaar is’. In de taal van de database is dat een vergelijking tussen de ingelogde gebruiker, auth.uid(), en de kolom met de eigenaar.
Voor toevoegen, wijzigen en verwijderen maak je aparte policies. Bij toevoegen wil je bijvoorbeeld controleren dat iemand een nieuw project alleen op zijn eigen naam kan zetten, en niet op die van een ander. Bij verwijderen wil je misschien dat alleen de eigenaar dat mag, terwijl teamleden wel mogen wijzigen.
Een policy is dus geen technisch detail, maar een zakelijke beslissing: wie mag wat met welke gegevens? Schrijf die regels eerst in gewone zinnen op. Dan kan een developer, of je AI-tool, ze vertalen naar policies, en kun jij nagaan of ze kloppen.
Zes veelgemaakte fouten
De meeste problemen met RLS ontstaan niet omdat iemand het niet wil, maar omdat de app ‘ineens niets meer liet zien’ en iemand, mens of AI, de snelste oplossing koos. Dit zijn de fouten die je het vaakst ziet:
- 1RLS staat uit. Een tabel die je met SQL aanmaakt, heeft RLS in PostgreSQL niet vanzelf aan. Wie dat vergeet, heeft een tabel die voor iedereen met de publieke sleutel open staat.
- 2RLS staat aan, maar zonder policies. Dan ziet niemand via de publieke sleutel nog iets. Dat is veilig, maar de app werkt niet, en de verleiding is groot om RLS dan maar weer uit te zetten.
- 3Een policy die alles toestaat. Een regel als ‘true’, oftewel ‘iedereen mag alles’, laat de waarschuwing in het dashboard verdwijnen, maar beschermt niets.
- 4De geheime sleutel in de browser. Bijvoorbeeld omdat hij in een instelling met VITE_ of NEXT_PUBLIC_ ervoor staat. Zulke instellingen komen bewust in de code voor de browser terecht.
- 5De bestandsopslag vergeten. Bestanden in Storage hebben hun eigen policies. Een bucket die op openbaar staat, is leesbaar voor iedereen die de link heeft.
- 6Views die RLS omzeilen. Een view is een opgeslagen query. Standaard draait die met de rechten van de maker, en volgt hij de policies van de onderliggende tabellen niet. Op PostgreSQL 15 en hoger los je dat op met de instelling security_invoker.
Testen als een andere gebruiker
Policies schrijven is één ding, weten dat ze werken is iets anders. Test daarom altijd zoals een echte gebruiker de app gebruikt. Maak twee testaccounts aan, zet met allebei wat gegevens in de app en kijk of het ene account iets van het andere kan zien of wijzigen. Probeer het daarna nog een keer zonder in te loggen.
Let op met de SQL-editor in het dashboard. Die draait standaard met de rechten van de beheerder van de database, en daarvoor gelden de policies niet. Een query die daar alle rijen teruggeeft, zegt dus niets over wat een gebruiker ziet. Supabase heeft wel een functie om je voor te doen als een bepaalde gebruiker of rol, in de Table Editor en de SQL-editor. Gebruik die, of test via de app zelf.
Doe deze test niet één keer, maar na elke wijziging aan je database. Een nieuwe tabel die je AI-tool aanmaakt, heeft ook nieuwe policies nodig.
Wanneer je hulp inschakelt
Heeft je app een handvol tabellen en geen gevoelige gegevens, dan kom je met de stappen hierboven een heel eind. Werk je met persoonsgegevens, betalingen of gegevens van bedrijven, of heeft je app meerdere soorten gebruikers, zoals klanten, medewerkers en beheerders, dan wil je dat iemand die policies kan lezen ze een keer naloopt.
Dat doe ik in de App Check. Voor €600 (excl. btw) controleer ik binnen 5 werkdagen je database, je policies, je sleutels en je bestandsopslag, samen met de rest van je app. Je krijgt een rapport in gewone taal met de risico’s in rood, oranje en groen, en we lopen het in 30 minuten samen door. Start je daarna binnen 30 dagen een onderhoudsplan, dan verreken ik de €600 volledig. In dat plan houd ik je app daarna bij, met monitoring, back-ups en een dagelijkse controle op bekende kwetsbaarheden.
Veelgestelde vragen
- Is het erg dat mijn publieke Supabase-sleutel in de browser te zien is?
- Nee, zo is Supabase ontworpen. Die sleutel kan alleen wat je policies toestaan. Het wordt pas een probleem als RLS op een tabel uitstaat of als een policy alles voor iedereen toestaat.
- Mijn app toont niets meer nadat ik RLS aanzette. Wat nu?
- Dat is normaal: met RLS aan en zonder policies is een tabel dicht. Zet RLS niet weer uit, maar voeg policies toe die precies beschrijven wie welke rijen mag lezen en wijzigen, en test ze met twee accounts.
- Geldt RLS ook voor bestanden die gebruikers uploaden?
- Ja, maar apart. Bestanden in Supabase Storage worden beschermd met policies op de opslag zelf. Een bucket die op openbaar staat, is voor iedereen met de link leesbaar, ook als je tabellen goed zijn beveiligd.
- Kan mijn AI-tool de policies voor me schrijven?
- Ja, een AI-tool kan prima een eerste versie maken, zeker als je de regels eerst in gewone zinnen beschrijft. Test ze daarna altijd zelf als echte gebruiker, want een policy die er goed uitziet, kan toch te ruim zijn.
Weet zeker dat je database dicht zit
In de App Check controleer ik je RLS, je policies, je sleutels en je bestandsopslag, samen met de rest van je app. Binnen 5 werkdagen, voor een vaste prijs van €600.