Artikel vertaald naar Nederlands

Een klant kwam vorige maand naar ons met een Lovable-gegenereerde app die al live in productie was met betalende klanten. Ze wilden dat we een paar functies zouden toevoegen en "wat dingen zouden opschonen." Wat we vonden tijdens onze initiële codebase audit was... erg informatief. Blootgestelde API-sleutels in client-side code. Ontbrekende Row Level Security-beleidsregels op tabellen met gebruikersgegevens. Directe Supabase-aanroepen zonder validatie aan de serverzijde. En dit was geen incident -- we hebben nu zes verschillende Lovable-projecten geaudit, en de patronen zijn opmerkelijk consistent.

Laat me duidelijk zijn: Lovable is een indrukwekkend gereedschap voor prototypen. De snelheid waarmee het werkende UI genereert op basis van prompts in natuurlijke taal is echt opmerkelijk. Maar er is een enorme kloof tussen "werkend prototype" en "productieklaar applicatie," en die kloof is gevuld met beveiligingskwetsbaarheden die de meeste niet-technische oprichters niet eens kennen.

Dit artikel is een technische analyse van wat we hebben gevonden in meerdere Lovable codebase audits. Als je iets met Lovable hebt gebouwd en je ontvangt echte geld of gegevens van gebruikers, dan moet je dit lezen.

Inhoudsopgave

Lovable Codebase Audit: Security Issues, RLS Gaps, and Exposed API Keys

Wat Lovable eigenlijk genereert

Lovable genereert React-applicaties (meestal met Vite) met Tailwind CSS en shadcn/ui-componenten, verbonden met een Supabase backend. De stack zelf is solide -- we bouwen regelmatig met dezelfde tools in ons Next.js development werk. Het probleem zit niet in de technologiekeuzes. Het zit in hoe ze met elkaar verbonden zijn.

Hier is een typische Lovable-projectstructuur:

src/
├── components/
│   ├── ui/           # shadcn-componenten
│   ├── Dashboard.tsx
│   ├── Settings.tsx
│   └── ...
├── integrations/
│   └── supabase/
│       ├── client.ts  # ← Dit is waar problemen beginnen
│       └── types.ts
├── pages/
├── hooks/
└── lib/

De gegenereerde code is schoon en leesbaar. Ik geef Lovable daar krediet voor. Maar leesbaarheid is niet hetzelfde als veiligheid. De architectuur maakt keuzes die prima zijn voor een demo, maar gevaarlijk voor productie.

Laten we specifiek worden.

Het API-sleutelprobleem

Elk Lovable-project dat we hebben geaudit heeft Supabase-inloggegevens in de client-side code. Voordat de Supabase-verdedigers zich melden: ja, de anon-sleutel is ontworpen om openbaar te zijn. Supabase's documentatie stelt dit expliciet. De anon-sleutel is bedoeld voor gebruik in client-side code, en beveiliging hoort afgedwongen te worden via Row Level Security-beleidsregels.

Maar hier wordt het lelijk.

In drie van de zes projecten die we hebben geaudit, vonden we de Supabase service_role-sleutel:

  1. Hardcoded rechtstreeks in een hulpbestand
  2. Opgeslagen in een .env-bestand dat was ingecheckt in de GitHub-repo
  3. Waarnaar verwezen werd in een Supabase Edge Function die zonder verificatie toegankelijk was

De service_role-sleutel omzeilt alle RLS-beleidsregels. Als iemand dit krijgt, hebben ze volledige lees-/schrijftoegang tot uw hele database. Elke tabel. Elke rij. Gegevens van elke gebruiker.

// Werkelijk patroon dat we vonden in een Lovable-project (sleutels onleesbaar gemaakt)
import { createClient } from '@supabase/supabase-js'

// Dit stond in een bestand genaamd lib/admin.ts
// geïmporteerd en gebruikt in een client-side component
const supabaseAdmin = createClient(
  'https://xxxxx.supabase.co',
  'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...' // service_role-sleutel!
)

Dit werd niet direct door Lovable gegenereerd -- het werd toegevoegd door de oprichter toen hij admin-functionaliteit nodig had en Lovable vroeg om "een admin-paneel toe te voegen." Lovable stemde in, en omdat het geen concept van server-side versus client-side beveiligingsgrenzen heeft, plaatste het de sleutel waar het handig was.

Zelfs wanneer alleen de anon-sleutel wordt blootgesteld (zoals bedoeld), valt het beveiligingsmodel in elkaar als RLS niet correct is geconfigureerd. Wat ons naar de grote brengt.

Row Level Security: De stille doder

Row Level Security (RLS) is het primaire beveiligingsmechanisme van Supabase voor het beveiligen van gegevens op databaseniveau. Wanneer correct geconfigureerd, zorgt het ervoor dat gebruikers ongeacht welke API-aanroepen vanuit de client worden gedaan, alleen hun eigen gegevens kunnen openen.

Toen we de zes Lovable-projecten auditeerden, vonden we het volgende:

Project Tabellen totaal Tabellen met RLS ingeschakeld Tabellen met correcte RLS-beleidsregels Gevoelige gegevens blootgesteld
SaaS Dashboard 14 6 3 Persoonlijke gegevens gebruikers, factureringsgegevens
E-commerce app 22 10 4 Ordergeschiedenis, adressen
Health Tracker 11 4 2 Gezondheidsgegevens, medicatie
Project Manager 18 8 5 Klantgegevens, documenten
Booking Platform 16 7 3 Contactinformatie, schema's
CRM-tool 20 9 4 Klantgegevens, aantekeningen

Lees dat nog een keer. In de health tracker-app -- die echte medicatie- en gezondheidsgegevens opsloot -- hadden slechts 2 van de 11 tabellen correcte RLS-beleidsregels. Iemand met de anon-sleutel (die publiek is, onthoud dat) kon gezondheidsgegevens van elke gebruiker opvragen.

De meest voorkomende RLS-fouten die we zien:

Volledig ontbrekende beleidsregels

Lovable maakt vaak tabellen aan zonder RLS in te schakelen. In Supabase is RLS standaard uitgeschakeld op nieuwe tabellen, wat betekent dat iedereen met de anon-sleutel alle gegevens kan lezen.

-- Wat we vaak vinden: RLS niet eens ingeschakeld
CREATE TABLE public.user_profiles (
  id UUID REFERENCES auth.users,
  full_name TEXT,
  email TEXT,
  phone TEXT,
  created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Geen ALTER TABLE ... ENABLE ROW LEVEL SECURITY;
-- Geen beleidsregels gedefinieerd

Te permissieve beleidsregels

Wanneer Lovable RLS toevoegt, zijn de beleidsregels vaak te breed:

-- Veelgebruikt Lovable-gegenereerd beleid
CREATE POLICY "Users can view all profiles"
  ON public.user_profiles
  FOR SELECT
  USING (true);  -- Dit stelt ELKE geverifieerde gebruiker in staat ALLE profielen te lezen

De fix zou moeten zijn:

-- Hoe het zou moeten zijn
CREATE POLICY "Users can view own profile"
  ON public.user_profiles
  FOR SELECT
  USING (auth.uid() = id);

Ontbrekende DELETE- en UPDATE-beleidsregels

Zelfs wanneer SELECT-beleidsregels correct zijn, ontbreken INSERT/UPDATE/DELETE-beleidsregels vaak of zijn ze fout. We vonden gevallen waarin elke geverifieerde gebruiker het profiel van elke andere gebruiker kon bijwerken.

Lovable Codebase Audit: Security Issues, RLS Gaps, and Exposed API Keys - architecture

Verificatie- en autorisatiegaten

Lovable verwerkt basisverificatie redelijk goed -- het stelt Supabase Auth in met email/wachtwoord of sociale inloggingen, en de login/signup-flows werken over het algemeen. Maar verificatie (wie ben je?) en autorisatie (wat mag je doen?) zijn verschillende dingen.

De autorisatielaag is bijna altijd onvolledig of niet aanwezig.

Beschouw een multi-tenant SaaS-app. Gebruikers behoren tot organisaties. Ze zouden alleen gegevens van hun organisatie moeten zien. Ze kunnen verschillende rollen hebben (admin, lid, viewer). Lovable genereert niets hiervan.

Wat we meestal vinden:

// Lovable-gegenereerde gegevensophaling
const { data: projects } = await supabase
  .from('projects')
  .select('*')
  // Geen filter voor organization_id
  // Geen controle op rol of machtigingen van gebruiker

De fix vereist zowel database-niveau (RLS) als application-niveau wijzigingen. Je hebt een memberships-tabel nodig die gebruikers aan organisaties met rollen toewijst, RLS-beleidsregels die lidmaatschap controleren, en applicatiecode die op passende wijze filtert.

Dit is het soort architectuurwerk dat moeilijk achteraf kan worden geïmplementeerd. Het raakt elke query, elke component, elke pagina aan. Als je een multi-tenant SaaS bouwt met Lovable, is dit het ding dat je het hardst zal bijten.

Blootstelling van client-side bedrijfslogica

Omdat Lovable zuivere client-side React-apps genereert, leeft alle bedrijfslogica in de browser. Dit betekent:

  • Prijsberekeningen zijn zichtbaar en manipuleerbaar in browser DevTools
  • Feature flags voor verschillende abonnementniveaus worden aan de client-zijde gecontroleerd
  • Kortingscode- en validatielogica bevindt zich in de JavaScript-bundel
  • API-snelheidsbeperkingen bestaan niet (er is geen server om dit af te dwingen)

We vonden één Lovable-gegenereerde SaaS waarbij de abonnementsniveaucontrole volledig aan de client-zijde plaatsvond:

// Gevonden in component van een Lovable-project
const canAccessFeature = (feature: string) => {
  const plan = user?.subscription?.plan
  if (plan === 'pro') return true
  if (plan === 'basic' && BASIC_FEATURES.includes(feature)) return true
  return false
}

Een gebruiker kon deze functie eenvoudig wijzigen in de browserconsole -- of rechtstreeks de Supabase API aanroepen zonder deze controle -- en pro-functies openen op een basisplan. De database had geen beleidsregels die plangebaseerde toegang afdwongen.

Dit is een fundamentaal architectuurprobleem. Bedrijfslogica heeft een server-side component nodig. Of dat Next.js API-routes, Supabase Edge Functions of een aparte backend-service is -- iets moet operaties valideren waar de gebruiker niet mee kan knoeien.

Dit is precies waarom we vaak frameworks als Next.js of Astro aanbevelen voor productie SaaS-applicaties -- ze geven je server-side rendering en API-routes uit de doos.

Supabase Edge Functions: Wat ontbreekt

Sommige Lovable-projecten gebruiken Supabase Edge Functions voor bepaalde operaties -- meestal Stripe webhook-verwerking of e-mail verzenden. Maar de implementatie heeft vaak problemen:

  1. Geen invoervalidatie: Edge Functions accepteren en verwerken welke JSON dan ook die naar hen wordt gestuurd zonder vorm, typen of beperkingen te valideren.

  2. CORS geconfigureerd om alle origins toe te staan:

// Veelgebruikt patroon in Lovable Edge Functions
const corsHeaders = {
  'Access-Control-Allow-Origin': '*',  // Staat elke website toe dit aan te roepen
  'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
}
  1. Geen verificatiecontrole: De functie verifieert de JWT-token niet, dus niet-geverifieerde gebruikers kunnen dit aanroepen.

  2. Stripe webhook-handtekeningen niet geverifieerd: In twee projecten verifieerde de Stripe webhook-handler de webhook-handtekening niet, wat betekent dat iedereen nep-betaaleventen naar het eindpunt kon sturen.

// Wat we vonden -- geen handtekeningverificatie
Deno.serve(async (req) => {
  const body = await req.json()
  // Verwerkt het evenement rechtstreeks zonder te verifiëren dat het van Stripe afkomstig is
  if (body.type === 'checkout.session.completed') {
    // Werk abonnement van gebruiker bij
    await supabaseAdmin.from('subscriptions').update({
      status: 'active',
      plan: 'pro'
    }).eq('user_id', body.data.object.metadata.user_id)
  }
})

Dit betekent dat een aanvaller een POST-verzoek naar de webhook URL kon sturen met een nep checkout.session.completed-evenement en elke gebruiker gratis upgraden naar een pro-plan.

Veelgebruikte kwetsbaarheidspatronen die we vonden

Hier is een samenvatting van de meest voorkomende problemen gerangschikt op ernst en frequentie:

Kwetsbaarheid Ernst Frequentie (uit 6) Uitbuitbaarheid
Ontbrekende RLS op gevoelige tabellen Kritiek 6/6 Gemakkelijk -- query de tabel gewoon
Te permissieve RLS-beleidsregels Hoog 6/6 Gemakkelijk met anon-sleutel
Blootstelling service_role-sleutel Kritiek 3/6 Triviaal indien gevonden
Geen Stripe webhook-verificatie Hoog 4/6 Gemiddeld -- eindpunt-URL nodig
Alleen client-side autorisatie Hoog 6/6 Gemakkelijk met DevTools
Geen invoervalidatie op Edge Functions Gemiddeld 5/6 Gemiddeld
CORS wildcard op Edge Functions Gemiddeld 5/6 Gemakkelijk
Gevoelige gegevens in localStorage Gemiddeld 4/6 Fysieke toegang of XSS
Geen snelheidsbeperkingen Gemiddeld 6/6 Triviaal
Onveilige directe objectverwijzingen Hoog 5/6 Gemakkelijk -- ID in URL wijzigen

Je eigen Lovable-project auditen

Als je iets met Lovable hebt gebouwd dat echte gebruikersgegevens afhandelt, kun je op deze manier naar deze problemen zoeken.

Stap 1: Controleer je Supabase RLS-status

Ga naar je Supabase-dashboard → Tabeleditor. Klik op elke tabel en controleer of RLS is ingeschakeld. Ga vervolgens naar Verificatie → Beleidsregels en controleer elk beleid.

Of voer deze query uit in de SQL-editor:

SELECT
  schemaname,
  tablename,
  rowsecurity
FROM pg_tables
WHERE schemaname = 'public'
ORDER BY tablename;

Als rowsecurity false is voor een tabel met gebruikersgegevens, is dat een kritiek probleem.

Stap 2: Zoek naar blootgestelde sleutels

Zoek in je codebase naar:

grep -r "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" src/
grep -r "service_role" src/
grep -r "SUPABASE_SERVICE" src/

Het eerste patroon komt overeen met het begin van Supabase JWT-sleutels. Als je de service_role-sleutel ergens in je src/-map vindt, is dat een onmiddellijk kritiek beveiligingsprobleem.

Stap 3: Test RLS-beleidsregels

Maak een tweede testgebruikersaccount. Log in als die gebruiker en probeer gegevens van gebruiker één op te vragen. Controleer het tabblad Netwerk van de browser -- ontvang je gegevens die je niet hoort te hebben?

Je kunt het ook rechtstreeks testen met curl:

curl 'https://YOUR_PROJECT.supabase.co/rest/v1/user_profiles?select=*' \
  -H "apikey: YOUR_ANON_KEY" \
  -H "Authorization: Bearer YOUR_ANON_KEY"

Als dit alle gebruikersprofielen teruggeeft zonder verificatie, is RLS verbroken.

Stap 4: Controleer Edge Functions

Controleer elke Edge Function op:

  • JWT-verificatie
  • Invoervalidatie
  • CORS-configuratie
  • Webhook-handtekeningverificatie (voor Stripe/betalingshandlers)

Stap 5: Inspecteer client-side bundel

Voer npm run build uit en inspecteer de uitvoer. Zoek in het gebouwde JavaScript naar API-sleutels, bedrijfslogica en prijsgegevens. Alles in de bundel is zichtbaar voor gebruikers.

Wanneer helemaal opnieuw bouwen versus wanneer aanpassen

Dit is de vraag waar elke Lovable-oprichter mee wordt geconfronteerd zodra hij zich deze problemen realiseert. Het antwoord hangt af van verschillende factoren:

Pas aan als:

  • Je app minder dan 10 tabellen heeft
  • Je hebt geen complex autorisatiemodel (geen multi-tenancy, geen rollen)
  • De kernarchitectuur (gegevensmodel, paginastructuur) is solide
  • Je hoeft vooral RLS-beleidsregels toe te voegen en logica naar de server-zijde te verplaatsen

Bouw helemaal opnieuw als:

  • Je hebt multi-tenant-architectuur met rollen en machtigingen nodig
  • Je bedrijfslogica is complex en allemaal aan de client-zijde
  • Je hebt server-side rendering nodig voor SEO of prestaties
  • Het Supabase-schema heeft aanzienlijke structurele problemen
  • Je bent op een schaal waar je een goede infrastructuur nodig hebt

Voor aanpassingen kijk je meestal naar het toevoegen van RLS-beleidsregels over alle tabellen, het verplaatsen van gevoelige logica naar Edge Functions of een server-side laag, het implementeren van juiste invoervalidatie en het toevoegen van snelheidsbeperkingen. Dit is een project van 2-4 weken voor een ervaren ontwikkelaar, afhankelijk van complexiteit.

Voor een volledig helemaal nieuw bouwwerk raden we meestal een headless-architectuur aan met juiste scheiding van verantwoordelijkheden. Dit kost meer aan de voorkant, maar geeft je een basis die schaalt. Kijk op onze prijzenpagina voor een gevoel van hoe dat eruitziet.

Als je onzeker bent welk pad het juiste is, kunnen we graag een snelle beoordeling doen. Neem contact met ons op via onze contactpagina.

Veelgestelde vragen

Is Lovable veilig voor productietoepassingen? Lovable kan een stevige uitgangspositie genereren, maar de uitvoer moet aanzienlijk beveiligd worden voordat het productieklaar is. De gegenereerde code mist juiste RLS-beleidsregels, server-side validatie en autorisatielogica. Beschouw het als steigering, niet als het afgebouwde gebouw. Je hebt absoluut een ontwikkelaar nodig om de code te controleren en te beveiligen voordat echte gebruikers dit vertrouwen met hun gegevens.

Stelt Lovable mijn Supabase API-sleutels bloot? De Supabase anon-sleutel is opzettelijk publiek -- dat is door ontwerp, en Supabase's beveiligingsmodel houdt daar via RLS rekening mee. Het probleem is wanneer Lovable (of jij, door prompts) de service_role-sleutel in client-side code plaatst. De anon-sleutel publiek zijn is alleen veilig als je RLS-beleidsregels waterdicht zijn, wat ze in Lovable-gegenereerde projecten meestal niet zijn.

Wat is Row Level Security en waarom is het belangrijk? Row Level Security (RLS) is een PostgreSQL-functie die Supabase gebruikt om te controleren welke rijen een gebruiker kan lezen, invoegen, bijwerken of verwijderen. Zonder RLS kan iedereen met je openbare anon-sleutel je hele database opvragen -- gegevens van elke gebruiker, elke privégegevens. Het is het enige belangrijkste beveiligingsmechanisme in een Supabase-ondersteunde applicatie, en het is het enige meest veelgebruikte foutief geconfigureerde ding in Lovable-projecten.

Kan ik Lovable-beveiligingsproblemen zelf zonder een ontwikkelaar oplossen? Als je SQL en Supabase's RLS-beleidssyntaxis begrijpt, kunt je jezelf basisbeleid toevoegen met behulp van het Supabase-dashboard. Het juist krijgen van beleidsregels -- vooral voor complexe scenario's zoals multi-tenancy, gedeelde bronnen of beheerderstoegang -- vereist ervaring. Een onjuist beleid kan gebruikers uit hun eigen gegevens sluiten of alles blootstellen. Voor iets meer dan een eenvoudig persoonlijk project moet je professioneel advies krijgen.

Hoe controleer ik of de database van mijn Lovable-app veilig is? De snelste test: open je browser DevTools, ga naar het tabblad Netwerk en kijk naar de Supabase API-aanroepen die je app maakt. Kopieer de apikey header-waarde. Gebruik vervolgens curl of Postman om je tabellen rechtstreeks op te vragen met alleen die sleutel zonder verificatietoken. Als je gegevens van andere gebruikers terugkrijgt -- of überhaupt gegevens op tabellen die privé zouden moeten zijn -- is je RLS verbroken.

Wat zijn de grootste beveiligingsrisico's met AI-gegenereerde code in het algemeen? AI-codegeneratoren optimaliseren voor het werkend maken van dingen, niet voor beveiligheid. Ze hebben geen mentaal model van je dreigingslandschap. De grootste risico's zijn: blootgestelde geheimen, ontbrekende invoervalidatie, te permissieve toegangscontroles en de afwezigheid van server-side beveiligingsgrenzen. Dit is niet uniek voor Lovable -- vergelijkbare problemen bestaan in code van Cursor, v0, Bolt en andere AI-tools. Het verschil is dat Lovable volledige applicaties genereert die mensen rechtstreeks naar productie implementeren.

Moet ik van Supabase overstappen naar een ander backend na het gebruiken van Lovable? Supabase zelf is prima. Het is een solide platform met juiste beveiligingsmogelijkheden. Het probleem is hoe Lovable het configureert. Je hoeft Supabase niet op te geven -- je hoeft RLS-beleidsregels correct in te stellen, gevoelige operaties naar Edge Functions te verplaatsen en de autorisatielaag toe te voegen die Lovable oversloeg. De infrastructuur is goed; de configuratie heeft gewoon werk nodig.

Hoeveel kost het om beveiligingsproblemen in een Lovable-gegenereerde app op te lossen? Voor een eenvoudige aanpassing -- RLS-beleidsregels toevoegen, Edge Functions beveiligen, blootgestelde sleutels verwijderen, basisinvoervalidatie toevoegen -- kijk je naar ongeveer €2.700-€7.200 afhankelijk van het aantal tabellen en complexiteit van je autorisatiemodel. Een volledige helemaal nieuw bouwwerk met juiste architectuur kost €13.500-€45.000+ afhankelijk van reikwijdte. Het aanpassingspad is bijna altijd kosteneffectief als je kerngegevensmodel solide is.


Vastgelopen met een Lovable-redding?

We redden kapotte Lovable-apps -- RLS-misconfiguaties, blootgestelde API-sleutels, oneindige bugcycli, schalimgrenzen. Vaste-omvang reddingssprints vanaf €4.500 / USD 6.5K. Zie de Lovable App Rescue-service →