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