Waarom RLS onmisbaar is in multi-tenant apps
Row-Level Security (RLS) is PostgreSQL's mechanisme om toegangscontrole op rijniveau af te dwingen. In een multi-tenant SaaS — waarbij meerdere bedrijven of gebruikers data in dezelfde database opslaan — is RLS geen optionele beveiligingslaag. Het is de fundamentele beveiligingsgarantie.
Zonder RLS: als je applicatielaag een bug heeft (een ontbrekend filter in een query), kunnen gebruikers data zien die niet van hen is. Dit is niet hypothetisch — het is de meest voorkomende oorzaak van datalekken in SaaS-producten.
Met RLS: zelfs als je applicatielaag een bug heeft, weigert de database rijen te retourneren waartoe de gebruiker geen toegang heeft. RLS is een vangnet op het laagste niveau.
Vanuit een AVG-perspectief vereist Artikel 25 (privacy by design) dat toegangscontrole in de architectuur is ingebakken, niet alleen op applicatieniveau wordt geïmplementeerd. RLS voldoet hier perfect aan.
RLS-policies schrijven: de basis
Activeer RLS op een tabel en schrijf je eerste policy:
```sql -- RLS activeren alter table orders enable row level security;
-- Gebruikers kunnen alleen hun eigen orders lezen create policy "users_own_orders" on orders for select using (user_id = auth.uid());
-- Voor multi-tenant: workspace-isolatie create policy "workspace_isolation" on projects for all using ( workspace_id in ( select workspace_id from workspace_members where user_id = auth.uid() ) ); ```
Kernregel: elke gebruikergerichte tabel heeft RLS nodig. Uitzondering: lookup-tabellen (landen, categorieën) die openbare data bevatten — die kunnen zonder RLS.
Test je RLS-policies expliciet: log in als gebruiker A en verifieer dat je alleen rijen met workspace_id = A ziet. Log in als gebruiker B en verifieer hetzelfde. Vertrouw niet op handmatige code-review — run tests.
Veelgemaakte RLS-fouten
**Fout 1: RLS activeren maar vergeten policies te schrijven**. Als RLS is geactiveerd maar er geen policies zijn, is de tabel volledig ontoegankelijk voor alle gebruikers — inclusief degenen die er toegang toe zouden moeten hebben. Altijd policies schrijven na het activeren van RLS.
**Fout 2: Vergeten om INSERT en UPDATE-policies te schrijven naast SELECT**. Veel developers schrijven alleen een SELECT-policy. Maar je hebt aparte policies nodig voor `INSERT`, `UPDATE` en `DELETE`. Een ontbrekende `INSERT`-policy kan ertoe leiden dat gebruikers rijen invoegen met een workspace_id die niet de hunne is.
**Fout 3: Performante policies niet optimaliseren**. RLS-policies worden uitgevoerd bij elke query. Een policy die een subquery uitvoert (zoals het controleren van workspace-lidmaatschap) kan traag zijn als de workspace_members-tabel groot is. Voeg indexen toe op de kolommen in je RLS-policy WHERE-clausules.
**Fout 4: Service role gebruiken vanuit de frontend**. De Supabase service role bypast RLS volledig. Gebruik nooit de service role sleutel in je WeWeb- of FlutterFlow-app. Gebruik altijd de `anon` of `authenticated` sleutel.
AVG-dataresidentievereisten
De AVG vereist niet expliciet dat data in de EU moet worden opgeslagen, maar het vergemakkelijkt compliance voor internationale datatransfers aanzienlijk. Voor Nederlandse B2B-SaaS is EU-dataresidentie steeds vaker een contractuele vereiste van enterprise-klanten.
Keuzes voor EU-dataresidentie:
**Supabase**: Kies `eu-west-1` (Frankfurt) bij het aanmaken van het project. Let op: je kunt de regio niet wijzigen na aanmaken. Al je data — inclusief database, storage en Edge Function logs — blijft in de EU.
**Xano**: Biedt EU-regio's aan op betaalde plannen. Kies Europa bij het instellen van je workspace.
**Make/n8n**: Make's servers zijn gebaseerd in de VS maar data die erdoor gaat kan tijdelijk buiten de EU worden verwerkt. Voor scenario's met gevoelige persoonlijke data gebruik je n8n self-hosted in de EU of Supabase Edge Functions als alternatief.
AVG-verplichtingen voor no-code SaaS
De acht verplichtingen die Nederlandse no-code SaaS-bedrijven het vaakst negeren:
1. **Verwerkingsregister**: Documenteer welke data je verwerkt, waarvoor en hoe lang je het bewaart 2. **Verwerkersovereenkomsten (DPA's)**: Onderteken DPA's met Supabase, Xano, Make, Stripe, en elke andere tool die persoonlijke data verwerkt 3. **Cookiebanner**: Voor analytics en marketing cookies op je marketingsite 4. **Privacybeleid**: Specifiek, niet generiek — beschrijf exact welke data je verzamelt en waarom 5. **Recht op inzage**: Procedure voor gebruikers om hun data op te vragen 6. **Recht op vergetelheid**: Bouw accountverwijdering in die alle persoonlijke data verwijdert 7. **Databreuk-procedure**: Weet hoe je een databreuk binnen 72 uur meldt bij de Autoriteit Persoonsgegevens 8. **Privacy by design**: RLS, minimale dataverzameling, versleutelde opslag voor gevoelige velden
Beveiligingsaudit checklist voor no-code apps
Voordat je een no-code SaaS lanceert voor betalende Nederlandse klanten:
**Database**: - [ ] RLS geactiveerd op alle gebruikergerichte tabellen - [ ] RLS-policies getest door in te loggen als verschillende gebruikers - [ ] Service role sleutel nooit in frontendcode - [ ] Supabase project in EU-west regio
**Frontend (WeWeb/FlutterFlow)**: - [ ] API-sleutels (OpenAI, Stripe, etc.) nergens in de frontend - [ ] HTTPS afgedwongen - [ ] Geen gevoelige data in URL-parameters of localStorage
**Backend (Xano/Edge Functions)**: - [ ] JWT-validatie op elk beveiligd endpoint - [ ] Invoervalidatie op alle parameters - [ ] Rate limiting op AI- en dure endpoints - [ ] Geen stack traces in productie-foutresponses
**Compliance**: - [ ] Privacybeleid gepubliceerd en correct - [ ] DPA's ondertekend met alle verwerkers - [ ] Accountverwijderingsflow gebouwd en getest - [ ] Cookiebanner geïmplementeerd