Lekt Jouw Vibe-Gecodeerde App Data en Verslindt Ze Je Budget?

Vijf vibe-coding faalmodi die leiden tot datalekken en oplopende cloudkosten

Je opent de ochtend na de lancering je dashboard, in de verwachting je eerste gebruikers te zien. In plaats daarvan vind je een cloudrekening met drie extra nullen erop, of erger nog, een bericht van een vreemdeling die je vertelt dat je volledige gebruikerstabel in een openbaar Telegram-kanaal staat.

Dit is geen zeldzaam horrorverhaal meer. Het is het stille, terugkerende faalpatroon van “vibe coding”: echte producten bouwen door AI-tools zoals Claude Code, Cursor en Windsurf te prompten totdat de UI er goed uitziet en de functie werkt. Deze tools zijn echt uitstekend in het snel schrijven van werkende code. Wat ze niet automatisch goed kunnen, is denken als een beveiligingsingenieur, omdat je ze dat nooit hebt gevraagd.

De UI kan productieklaar ogen terwijl de architectuur eronder wagenwijd openstaat. Hieronder staan vijf fouten die we het vaakst zien wanneer AI-ondersteunde codebases het echte internet raken, waarom elke fout snel duur wordt, en precies hoe je ze sluit voordat je publiceert.

Het ongemakkelijke deel: geen van deze vijf fouten vereist een hacker. Een nieuwsgierige bezoeker die F12 indrukt in zijn browser, een verveelde bot die willekeurige domeinen scant, of één enkele vergeten achtergrondtab is genoeg om elk van deze fouten te veroorzaken. Dat is wat ze zo veelvoorkomend maakt, en zo vermijdbaar, in AI-gegenereerde codebases.

Waarom Vibe-Gecodeerde Apps Harder Worden Geraakt Dan Traditioneel Gebouwde Apps

Wanneer een ontwikkelaar handmatig een API-integratie schrijft, is beveiliging een bewuste beslissing die op dat moment wordt genomen: “moet deze sleutel op de server of de client staan?” Wanneer een AI-assistent dezelfde code schrijft, optimaliseert deze voor “werkt dit wanneer ik het test”, niet voor “houdt dit stand wanneer een vreemdeling het inspecteert”.

Dat verschil is het hele probleem. AI-gegenereerde code draait prima in preview, prima in je klantdemo, en blijft prima draaien tot aan de lancering, wanneer iemand met niets meer dan de F12-console van zijn browser een controle van vijf seconden uitvoert.

Fout 1: Hardgecodeerde API-Sleutels in Je Frontend-Code

Dit is de meest voorkomende manier waarop een startende oprichter wakker wordt met een leeggezogen OpenAI- of Anthropic-account.

Vraag een AI-assistent om “verbinding te maken met de OpenAI API” of “een Supabase-client toe te voegen”, en het zal de sleutel vaak rechtstreeks in een React-component, een Next.js-pagina of een client-side .js-bestand schrijven, omdat dat de snelste manier is om de functie te laten werken. Het compileert. Het werkt in preview. Je lanceert het.

Het probleem is dat alles wat naar de browser wordt verzonden, downloadbaar is door iedereen die je site bezoekt. Een bezoeker drukt op F12, opent het tabblad Sources of Network, en jouw sk-...-sleutel of Supabase service_role-sleutel staat daar gewoon in platte tekst. Eén blootgestelde sleutel is vaak alles wat nodig is om een heel maandbudget aan AI binnen enkele minuten leeg te zuigen, en als het je service_role-sleutel is, kan de aanvaller elke rechtenregel omzeilen die je database zou moeten afdwingen.

Hoe je het daadwerkelijk oplost:

  • Roep nooit een betaalde of bevoorrechte API rechtstreeks aan vanuit client-side code. Route het via een backend-eindpunt of serverless functie die je zelf beheert.
  • Bewaar echte geheimen in server-side omgevingsvariabelen, nooit in iets dat wordt gebundeld in de frontend-build. In Supabase hoort alleen de publieke anon-sleutel thuis in frontend-code.
  • Roteer een sleutel onmiddellijk als je ontdekt dat deze ooit is blootgesteld, zelfs kortstondig, in een openbare repo of een gepubliceerde bundel.

Fout 2: Row Level Security Uitgeschakeld Gelaten op Supabase of Firebase

Dit is de fout die “we hadden een bug” verandert in “we hadden een datalek”.

Supabase en Firebase geven je allebei een echte database die rechtstreeks bereikbaar is vanuit de browser, wat krachtig is voor snel bouwen. Maar die kracht komt met een voorwaarde: Row Level Security (RLS) moet worden geconfigureerd om aan te geven wie welke rijen mag lezen en schrijven. AI-codeassistenten leiden je vaak door de setup met RLS in een toegeeflijke of volledig openbare staat, omdat een openbaar beleid de snelste manier is om je functie tijdens de ontwikkeling “gewoon te laten werken”.

Het addertje onder het gras is dat een openbaar RLS-beleid niet alleen betekent dat ingelogde gebruikers meer kunnen lezen dan zou moeten. Het betekent dat iedereen, zonder in te loggen, een browserconsole kan openen en elke rij in elke blootgestelde tabel kan ophalen door een rauw fetch-verzoek rechtstreeks naar de publieke API-URL van je database te sturen. Je volledige gebruikerslijst, elke bestelling, elk privébericht, allemaal op te vragen met een verzoek dat minder tijd kost om te schrijven dan deze zin.

Hoe je het daadwerkelijk oplost:

  • Behandel RLS als verplicht, niet optioneel, voordat er echte gebruikersdata in de database komt. Schakel het in op elke tabel die niet volledig openbaar hoeft te zijn.
  • Schrijf beleidsregels die zijn afgestemd op auth.uid() (Supabase) of de gelijkwaardige geauthenticeerde controle (Firebase), zodat een gebruiker alleen zijn eigen rijen kan raken.
  • Test beleidsregels als een anonieme, ongeauthenticeerde aanvraag, niet alleen vanuit je eigen ingelogde browsersessie waar je mogelijk verhoogde rechten hebt zonder het te beseffen.
  • Controleer RLS opnieuw telkens wanneer een nieuwe tabel wordt toegevoegd, aangezien deze niet automatisch wordt gedekt door regels die je voor andere tabellen hebt geschreven.

We behandelen het praktische beslissingsproces hiervoor uitgebreider in onze gids over wanneer row-level security toe te voegen aan een Supabase SaaS MVP, inclusief hoe je het gefaseerd invoert zonder de vroege ontwikkelsnelheid te blokkeren.

Fout 3: Oneindige Renderlussen Die Stilletjes API-Verzoeken Verslinden

Deze fout lijkt in eerste instantie niet op een beveiligingsbug. Het lijkt op een facturatiebug, en tegen de tijd dat je het opmerkt, staat de schade al op je factuur.

React’s useEffect-hook wordt opnieuw uitgevoerd wanneer er iets verandert in de dependency array. AI-assistenten krijgen die array soms verkeerd: een object of functie wordt bij elke render opnieuw aangemaakt, het effect vuurt opnieuw af, dat veroorzaakt een status-update, wat weer een render veroorzaakt, wat het effect opnieuw afvuurt. De lus herhaalt zich zo snel als de browser dat kan uitvoeren.

Als dat effect toevallig een API-aanroep, een Supabase-query of een aanroep naar een betaald AI-model bevat, krijg je niet één verzoek per paginalading. Je krijgt honderden of duizenden, vaak afkomstig van één enkele tab die één gebruiker ’s nachts vergat te sluiten op de achtergrond.

Praktijkgeval: één inactieve browsertab genereerde meer dan 2.000 API-verzoeken voordat iemand het opmerkte, simpelweg omdat een useEffect-dependency array verwees naar een vers aangemaakt object bij elke render in plaats van naar een stabiele waarde. Vermenigvuldig dat met een handvol vergeten tabbladen bij je vroege gebruikers, en een kleine bug wordt een rekening die je aan een medeoprichter moet uitleggen.

Hoe je het daadwerkelijk oplost:

  • Geef altijd een volledige, correcte dependency array op voor elke useEffect — ontbrekende afhankelijkheden en instabiele referenties (nieuwe objecten, arrays of functies die inline worden aangemaakt) zijn de twee meest voorkomende oorzaken van deze lus.
  • Gebruik het Network-tabblad van je browser tijdens het testen en let op een verzoekaantal dat blijft oplopen op een pagina waarmee je niet actief interactie hebt.
  • Voeg rate limiting of monitoring van het aantal verzoeken toe aan elk eindpunt dat een betaalde externe API raakt, zodat een op hol geslagen frontend-lus tegen een muur botst in plaats van tegen een open rekening.
  • Stel gebruikswaarschuwingen en harde uitgavenlimieten in rechtstreeks in je OpenAI-, Anthropic- en cloudprovider-dashboards. Dit stopt de bug niet, maar het voorkomt dat de bug een verrassing van €1.000 wordt.

Fout 4: Admin- en Interne Routes Gelanceerd Zonder Echte Authenticatie

Deze fout verstopt zich in het volle zicht omdat de functie perfect werkt voor jou, de enige persoon die weet dat de URL bestaat.

Vraag een AI-assistent om “een admindashboard om gebruikers te beheren” of “een interne API om bestelstatus bij te werken”, en het bouwt de pagina vaak correct terwijl het de authenticatiecontrole overslaat, omdat je daar nooit expliciet om hebt gevraagd. Het resultaat is een volledig functionele /admin-, /internal-api- of /debug-route op je productiedomein zonder enige inlogpoort, vertrouwend op het feit dat niemand de URL raadt.

Obscuriteit is geen beveiliging. Geautomatiseerde bots doorzoeken routinematig veelvoorkomende paden zoals /admin, /api/internal en /.env op elk domein dat ze vinden, zonder enig idee wat jouw product doet. Als de route antwoordt zonder te vragen wie er vraagt, wordt hij uiteindelijk gevonden, en wie hem vindt, krijgt dezelfde toegang als jij.

Hoe je het daadwerkelijk oplost:

  • Vereis authenticatie en rolcontroles op elke route die niet volledig openbaar hoeft te zijn, inclusief routes die je “alleen voor jezelf” hebt gebouwd tijdens de ontwikkeling.
  • Vertrouw nooit op een niet-vermelde of moeilijk te raden URL als je enige bescherming, behandel elke route als vindbaar.
  • Test elke admin- en interne route op dezelfde manier als je RLS hebt getest: als een uitgelogde, ongeauthenticeerde aanvraag, niet vanuit je eigen al ingelogde browsersessie.
  • Vraag je AI-assistent rechtstreeks: “controleert deze route authenticatie en rol voordat er data wordt teruggegeven?”, in plaats van aan te nemen dat dit zo is.

Fout 5: Geen Rate Limiting op Betaalde of Gevoelige Eindpunten

Dit is de fout die een nieuwsgierige bezoeker, of een bot die je API per ongeluk heeft gevonden, verandert in je grootste kostenpost van de maand.

Zelfs met beveiligde sleutels en ingeschakelde RLS, is een eindpunt dat onbeperkte verzoeken van onbeperkte bronnen accepteert nog steeds een open kraan. AI-gegenereerde backend-code wordt vaak functioneel maar ongedrosseld opgeleverd: een eindpunt dat een betaald AI-model aanroept of naar je database schrijft, verwerkt elk verzoek dat het ontvangt, zo snel als het ze ontvangt, zonder enig concept van “te veel, te snel”, tenzij iemand dat expliciet heeft ingebouwd.

In tegenstelling tot de op hol geslagen useEffect-lus (een onbedoelde bug in je eigen frontend), komt dit risico van buitenaf: een scraper of een bot die opzettelijk op je openbare eindpunt beukt, precies omdat niets hem tegenhoudt.

Hoe je het daadwerkelijk oplost:

  • Voeg rate limiting toe op eindpunt- of API-gatewayniveau voor elke route die je geld kost per aanroep of gevoelige data raakt, met een limiet op verzoeken per IP of per gebruiker binnen een tijdvenster.
  • Vereis authenticatie voordat een dure bewerking wordt uitgevoerd; een ongeauthenticeerd verzoek zou nooit een betaalde AI-aanroep of een bulk-databaseschrijving moeten kunnen triggeren.
  • Gebruik de ingebouwde throttling van je hostingplatform of API-gateway (Vercel, Cloudflare, Supabase Edge Functions en API-gateways zoals Kong of AWS API Gateway ondersteunen dit allemaal) in plaats van het zelf te proberen te bouwen.
  • Monitor op verzoekpieken van een enkel IP of gebruikers-ID; een plotselinge, aanhoudende piek is een signaal dat een waarschuwing verdient, niet alleen een factuurregel die je later opmerkt.

Snelle Vergelijking: Wat Elke Fout Je Daadwerkelijk Kost

Fout Wat Wordt Blootgesteld Typische Trigger Snelste Oplossing
Hardgecodeerde API-sleutels AI/API-credits, betaald gebruik Sleutel geplakt in frontend-code door AI-assistent Verplaats aanroepen naar een backend-eindpunt, roteer de sleutel
Uitgeschakelde Row Level Security Volledige database-inhoud Openbaar/toegeeflijk RLS-beleid overgebleven van setup Schakel RLS in, stem beleidsregels af op auth.uid()
Oneindige useEffect-lussen Cloud-/API-facturering Ontbrekende of instabiele dependency array Los dependency array op, voeg rate limits en uitgavenlimieten toe
Ongeauthenticeerde admin-/interne routes Volledige admintoegang, interne data Route gebouwd zonder authenticatiecontrole Voeg authenticatie- en rolcontroles toe aan elke route
Geen rate limiting op betaalde eindpunten Cloud-/API-facturering, databasebelasting Eindpunt open gelaten voor onbeperkte verzoeken Voeg rate limiting toe en vereis authenticatie voor dure aanroepen

De Zelfaudit van 5 Minuten Die Je Nu Kunt Uitvoeren

Je hebt geen beveiligingsteam nodig om het meeste hiervan te vangen. Doorloop deze controles zelf voor je volgende deploy:

  • Open DevTools op je live site (F12 → tabblad Sources of Network) en zoek naar sk-, service_role of een string die eruitziet als een databaseverbindings-URL. Als je er een vindt, roteer deze onmiddellijk en verplaats die aanroep server-side.
  • Open je Supabase- of Firebase-dashboard en controleer de RLS-status op elke tabel. Alles wat “public” of “disabled” toont naast een tabel met echte gebruikersdata, is een tabel die iedereen op dit moment kan opvragen.
  • Open je Network-tabblad op een pagina met een useEffect en laat het 30 seconden inactief. Als het verzoekaantal blijft oplopen zonder dat je ergens op klikt, heb je een lus, geen functie.
  • Controleer je OpenAI-, Anthropic- en cloudfacturatiedashboards op gebruikswaarschuwingen. Als je nog geen harde uitgavenlimiet hebt ingesteld, doe dit voordat je deze zin klaar leest, niet erna.
  • Probeer je admin-, interne of debugroutes te laden in een incognitovenster terwijl je bent uitgelogd. Als je de pagina nog steeds kunt zien of data terugkrijgt, heeft die route geen echte authenticatie.
  • Controleer of je betaalde of gevoelige eindpunten enige rate limiting hebben geconfigureerd. Als een script hetzelfde eindpunt 10.000 keer achter elkaar zou kunnen aanroepen zonder te worden vertraagd of geblokkeerd, dan heeft het die niet.

Vijf minuten nu is goedkoper dan elk scenario dat dit artikel beschrijft.

Dit Is Geen Reden Om te Stoppen met AI-Codeertools

Niets van dit alles betekent dat Claude Code, Cursor of Windsurf onveilige tools zijn, of dat AI-ondersteunde ontwikkeling moet worden vermeden. Ze zijn oprecht in staat productiewaardige code te produceren, inclusief veilige patronen, wanneer de persoon die ze aanstuurt op de juiste dingen controleert. De tools zijn niet het faalpunt. Lanceren zonder een op beveiliging gerichte review is dat wel.

Behandel elke AI-gegenereerde functie zoals je een pull request van een junior developer zou behandelen: waarschijnlijk prima, maar het verdient een tweede paar ogen op alles wat geheimen, databaserechten of betaalde API-aanroepen raakt voordat het live gaat. Die review kost een middag. Een rekening van €1.000 of een gelekte database opruimen kost veel langer, en kan veel meer kosten dan geld zodra klantvertrouwen in het spel is.

Als je al diep in een vibe-gecodeerde build zit en een gestructureerde manier zoekt om deze gaten te dichten voordat er echte gebruikers komen, loopt onze checklist over wat te doen nadat je jouw MVP vibe-codeert door de test-, beveiligings- en schaalstappen in volgorde. Het is ook de moeite waard om je hostingsetup te controleren aan de hand van veelvoorkomende cloudhostingfouten die de infrastructuurrekening van een startup opblazen, aangezien facturatieverrassingen zelden uit slechts één bron komen. En als je AI-assistent ooit heeft voorgesteld een pakket te installeren dat je niet herkende, lees dan over waarom AI-codeertools soms nepakketten verzinnen voordat je dat installatiecommando uitvoert.

Wil Je een Beveiligingsgerichte Review van Jouw Vibe-Gecodeerde MVP?

MVPHUB helpt oprichters om AI-gegenereerde prototypes om te zetten in veilige, productieklare producten, en spoort blootgestelde geheimen, databaserechten-lekken en op hol geslagen API-gebruik op voordat ze dure verrassingen worden. Boek een gratis consult met MVPHUB om je codebase te laten beoordelen voordat je opschaalt.

Boek een gratis consult met MVPHUB

Veelgestelde vragen

Waarom schoot mijn cloudrekening plotseling omhoog naar meer dan €1000 na de lancering van mijn app?

De meest voorkomende oorzaken zijn een hardgecodeerde API-sleutel die is gekopieerd en misbruikt door iemand anders, een oncontroleerbare lus in je frontend-code die duizenden dubbele verzoeken verstuurt, een openbare database waarmee externe scripts non-stop data konden ophalen, of een betaald eindpunt zonder rate limiting dat een bot heeft gevonden en overbelast. Elk van deze factoren vermenigvuldigt op gebruik gebaseerde kosten zeer snel, vaak binnen één nacht.

Is het veilig om Cursor, Claude Code of Windsurf te gebruiken voor het bouwen van een echt product?

Ja, deze tools zijn veilig in gebruik en kunnen de ontwikkeling aanzienlijk versnellen. Het risico zit niet in de tool zelf, maar in het lanceren zonder controle op beveiligingsgevoelige gebieden zoals de opslag van geheimen, toegangsregels voor de database en effect-afhankelijkheden die AI-assistenten niet altijd standaard goed afhandelen.

Wat is Row Level Security en waarom is het belangrijk voor vibe-gecodeerde apps?

Row Level Security (RLS) is een databasefunctie in tools zoals Supabase en Firebase die beperkt welke rijen een gebruiker mag lezen of schrijven. Wanneer RLS in de standaard open staat blijft staan, kan iedereen met jouw publieke API-URL rechtstreeks vanuit een browserconsole je hele database opvragen, zonder in te loggen.

Hoe weet ik of mijn API-sleutels zijn blootgesteld in mijn frontend-code?

Open je live site, druk op F12 om de browser DevTools te openen en controleer het tabblad Sources of Network op een sleutel die begint met sk-, service_role of een volledige databaseverbindingsreeks. Als jij het daar kunt zien, kan iedereen die je site bezoekt het ook zien.

Kan een useEffect-bug echt duizenden extra API-kosten veroorzaken?

Ja. Een ontbrekende of onjuiste dependency array in een React useEffect-hook kan ervoor zorgen dat een component voortdurend opnieuw rendert en zijn API-aanroep blijft afvuren. Onopgemerkt op een pagina die gebruikers open laten staan in een achtergrondtab, kan dit duizenden verzoeken per sessie genereren, met een bijbehorende piek in op gebruik gebaseerde facturering.

Moet ik een beveiligingsexpert inhuren voordat ik een vibe-gecodeerde MVP lanceer?

Niet per se voor een vroege MVP, maar je hebt wel iemand met software engineering inzicht nodig om architectuurrisico's te beoordelen voordat echte gebruikers en echte betaalmethoden het product raken. Een korte review gericht op geheimen, databaseregels en API-gebruikspatronen vangt het meeste op wat dure verrassingen veroorzaakt.

Hoe controleer ik of mijn admin- of interne routes daadwerkelijk beveiligd zijn?

Open de route in een incognito- of privévenster terwijl je volledig uitgelogd bent. Als je de pagina nog steeds kunt bekijken of er data van terugkrijgt, is er geen echte authenticatiecontrole aanwezig en is de route bereikbaar voor iedereen die de URL vindt, inclusief geautomatiseerde scanners die zoeken naar veelvoorkomende adminpaden.

Wat is rate limiting en waarom heeft een kleine MVP dit nodig?

Rate limiting begrenst hoeveel verzoeken een enkele gebruiker of IP-adres binnen een bepaald tijdvenster naar een eindpunt kan sturen. Zelfs een kleine MVP heeft dit nodig op elk eindpunt dat geld kost per aanroep of gevoelige data raakt, want zonder rate limiting kan een script of bot dat eindpunt zo vaak aanroepen als het wil, waardoor één onbeveiligde route een onbeperkte rekening wordt.

Heb je een goed idee?

Laat het niet bij een idee. Valideer het en bouw je MVP met ons ervaren engineeringteam.

Check mijn idee