Beveiliging per stack

Security voor AI-gebouwde apps

Vibe-coded apps staan binnen enkele uren online, maar de basisbeveiliging —blootgestelde secrets, ontbrekende databaseregels, open CORS, missende headers— blijft onderweg liggen. Deze gids legt zonder jargon uit welke fouten AI-gebouwde apps écht raken, waarom ze keer op keer terugkomen, hoe je ze zelf in tien minuten of met een scanner in 30 seconden controleert, en waar je na de lancering op moet letten.

Wat "vibe coding security" betekent

AI-tools (Lovable, Bolt, v0, Cursor, Replit, Windsurf) genereren apps met een heel specifieke architectuur: de browser praat rechtstreeks met de database (Supabase, Firebase) en met externe diensten (Stripe, OpenAI, Resend). Er zit vrijwel geen eigen server tussen. Dat versnelt alles, maar het verplaatst de beveiliging van je code naar je configuratie: wat je beschermt zijn de databaseregels, de HTTP-headers, het CORS-beleid en de plek waar je keys staan, niet hoe schoon de code van de interface is.

Die onderdelen zijn standaard niet goed ingesteld, en de AI configureert ze zelden als je er niet om vraagt. Haar doel is dat de app werkt in de demo, en een tabel zonder beperkingen of een key die in de frontend is geplakt "werken" perfect. Het resultaat zijn apps die er onberispelijk uitzien en data of keys blootgeven aan iedereen die weet waar hij moet kijken: je hoeft geen hacker te zijn, de ontwikkelaarstools van de browser openen is genoeg.

Daarom betekent "vibe coding security" niet dat je cryptografie moet leren of een audit moet inhuren. Het betekent de handvol fouten kennen die in bijna elke AI-gebouwde app terugkomen, weten hoe je ze van buitenaf controleert en dezelfde AI vragen om ze met precieze instructies op te lossen.

Waarom AI-gebouwde apps op dezelfde punten falen

Er is een patroon. De AI optimaliseert ervoor dat de code compileert en het scherm data toont, niet dat een vreemde die data niet kan zien. Als een query faalt op rechten, is de kortste oplossing het recht weghalen dat in de weg zit: Row Level Security uitzetten, een regel toevoegen die alles toestaat of de admin-key gebruiken. En de AI kiest de kortste oplossing.

De tweede reden is dat ze templates kopieert. Variabelen met het voorvoegsel NEXT_PUBLIC_, VITE_ of NUXT_PUBLIC_ worden bewust naar de browser gestuurd, en veel voorbeelden op internet zetten daar keys in die de server nooit zouden mogen verlaten. Firebase-regels in testmodus laten iedereen 30 dagen lezen en schrijven, en apps gaan live met die modus nog aan. Security headers komen in bijna geen enkele tutorial voor, dus komen ze ook niet in de app voor.

De derde is dat niemand het dreigingsmodel tekent. Wie met AI bouwt denkt aan de gebruiker die hij wil, niet aan de bezoeker die het Network-tabblad opent, de API-URL kopieert en het filter weghaalt. Deze gids gaat over tien minuten in de schoenen van die bezoeker staan.

De zeven fouten waar AI-gebouwde apps het meest last van hebben

API-keys in de browser. Als je een dienst "koppelt", belandt de key in client-side JavaScript, waar hij voor altijd openbaar is: iedereen kan hem lezen via de broncode en op jouw kosten gebruiken (OpenAI-rekeningen, e-mails verstuurd vanaf jouw domein, toegang tot je data). De enige keys die naar de client mogen zijn die daarvoor ontworpen zijn, zoals de anon- of publishable-key van Supabase, de publieke Stripe-key of de Firebase-configuratie, en alleen als de regels erachter kloppen.

Ontbrekende of zwakke databaseregels. Zonder Row Level Security op elke Supabase-tabel, of met een policy die alles toestaat, leest en beschrijft iedereen je data zonder in te loggen, via dezelfde API die je app gebruikt. In Firebase doen regels in testmodus of met allow read, write: if true hetzelfde. Het is de ernstigste en meest voorkomende fout, en het was de oorzaak van het lek CVE-2025-48757 dat gebruikersdata blootlegde in apps gemaakt met Lovable.

Te ruime CORS. Een API die met Access-Control-Allow-Origin elke origin weerspiegelt en daarbij credentials toestaat, laat een kwaadaardige site aanvragen doen namens je gebruikers en de antwoorden lezen. De wildcard op een publieke API zonder cookies is acceptabel; de origin weerspiegelen met credentials, nooit.

Ontbrekende security headers. Zonder Content-Security-Policy, Strict-Transport-Security, X-Frame-Options (of frame-ancestors), X-Content-Type-Options, Referrer-Policy en Permissions-Policy staat je app open voor clickjacking, scriptinjectie, downgrade naar HTTP en het lekken van interne URL's. Ze worden op de server of bij de host (Vercel, Netlify, Cloudflare) in tien regels ingesteld.

Toegankelijke gevoelige bestanden. Een per ongeluk gepubliceerde .env, een blootgestelde .git-map, source maps waarmee de volledige broncode te reconstrueren is, databaseback-ups of adminpanelen zonder authenticatie. Ze zijn makkelijk te vinden omdat aanvallers er op elk nieuw domein automatisch naar zoeken.

Verkeerd ingestelde sessies en cookies. Sessiecookies zonder de attributen Secure, HttpOnly en SameSite, tokens in localStorage binnen bereik van elk script, of sessies die nooit verlopen. Een kleine XSS wordt een accountovername als de cookie vanuit JavaScript leesbaar is.

Endpoints die de client vertrouwen. API-routes die de gebruikers-id, de prijs of de rol uit de request body overnemen zonder dat op de server te controleren. Als de interface de adminknop verbergt maar de route nog steeds iedereen antwoordt, beschermt die knop niets.

Wat je per stack moet nakijken

Lovable, Bolt of v0 met Supabase. Controleer dat alle tabellen RLS aan hebben en policies per operatie (select, insert, update, delete) op basis van auth.uid(); dat de service_role-key in geen enkele publieke variabele staat; dat privé-Storage-buckets policies hebben; en dat views en RPC-functies de policies niet omzeilen. Het Supabase-dashboard heeft een Security Advisor die het ergste opsomt.

Cursor, Windsurf of Replit met Next.js, Nuxt of Express op Vercel. Controleer welke omgevingsvariabelen een publiek voorvoegsel dragen, dat API-routes de sessie bij elke aanvraag valideren en niet alleen in de interface, dat de security headers zijn gedefinieerd (vercel.json, next.config of nuxt.config) en dat er in productie geen source maps worden gepubliceerd.

Firebase. Vervang de testmodusregels door regels op basis van request.auth, zowel in Firestore als in Storage; schakel App Check in zodat alleen jouw app je diensten kan gebruiken; en controleer dat Cloud Functions die iets gevoeligs doen het token van de gebruiker controleren voordat ze handelen.

Dat de app "er goed uitziet" betekent niet dat hij veilig is

Een knop verstoppen in de interface beschermt niets: de aanvraag blijft een openbare URL die iedereen rechtstreeks vanuit de browserconsole of met curl kan aanroepen. Echte beveiliging zit op de server en in de database, niet in wat de gebruiker ziet. Daarom is controleren van buitenaf —zoals een aanvaller zou doen— de enige manier om te weten wat er werkelijk blootligt.

Ook een groen HTTPS-slotje is niet genoeg. HTTPS versleutelt de weg tussen de browser en je server; het zegt niets over of de server data aan de verkeerde mensen geeft. Een app met een perfect certificaat en een tabel zonder RLS is een app met een perfect versleuteld lek.

Hoe je het zelf in tien minuten controleert

Eén: open je site en bekijk de broncode (rechtsklik, paginabron bekijken). Zoek naar key, secret, sk_, service_role en de namen van de diensten die je gebruikt. Alles wat daar verschijnt is openbaar.

Twee: ga met de app open naar het Network-tabblad van de ontwikkelaarstools, zoek een aanvraag naar je database of API, kopieer de URL en open hem in een incognitovenster. Geeft hij data terug zonder sessie, dan staat dat endpoint open. Haal ingelogd het gebruikersfilter uit de URL: geeft hij rijen van anderen terug, dan kloppen de policies niet.

Drie: vraag de headers van je domein op met curl -I https://jouwdomein.nl en controleer of Content-Security-Policy, Strict-Transport-Security en X-Frame-Options of frame-ancestors verschijnen. Ontbreekt er een, dan is hij niet ingesteld.

Vier: probeer /.env, /.git/config, /config.json en een .js.map-bestand uit je bundle te openen. Ze moeten allemaal 404 of 403 teruggeven.

Vijf: maak twee testaccounts aan en controleer dat geen van beiden de data van de ander ziet, bewerkt of verwijdert, geüploade bestanden incluis. Het is de saaiste test en degene die de meeste fouten vindt.

Hoe je het in 30 seconden controleert

Scanaris automatiseert die tests en nog een paar. Het scant je gepubliceerde site read-only: het raakt je code niet aan, injecteert niets en probeert niets uit te buiten; het kijkt naar wat al openbaar is, zoals iedereen van buitenaf zou kunnen. Het controleert headers, cookies, het TLS-certificaat, CORS, secrets in de JavaScript, gevoelige bestanden, technologieën met bekende versies, je Supabase-regels als je het een read-only verbinding geeft, en bovendien hoe Google je ziet en hoe AI's je lezen.

Elke bevinding komt gerangschikt op impact (kritiek eerst), uitgelegd in gewone taal en met een fixprompt die je zo in Lovable, Cursor of jouw tool plakt. Plak je URL, wacht een halve minuut en je weet of je je app met een gerust hart kunt delen.

Waar je na de lancering op moet letten

Beveiliging is geen toestand, het is een gewoonte. Elke nieuwe tabel wordt zonder RLS geboren, elke nieuwe functie kan een key op de verkeerde plek zetten en elke dependency veroudert. Plan een periodieke herscan en bekijk de verschillen, niet het hele rapport: wat telt is wat er sinds de vorige keer is opengegaan.

Let ook op wat niet van de code afhangt: dat het certificaat en het domein niet verlopen, dat de site reageert, en dat er in de browser van je gebruikers geen scripts verschijnen die jij niet hebt geplaatst. En als je iets oplost, controleer het dan opnieuw van buitenaf: de fix telt als de scanner hem niet meer ziet, niet als de AI zegt dat het klaar is.

Scan my site free

Duik dieper voor jouw stack

Belangrijkste beveiligingschecks

Gerelateerde gidsen

Veelgestelde vragen

Is mijn Lovable-, Bolt- of Cursor-app veilig?

Dat kan, maar standaard komt hij meestal online met keys in de frontend, onvolledige databaseregels en zonder security headers. De enige manier om het te weten is je gepubliceerde site controleren: Scanaris doet dat in 30 seconden, read-only.

Betekent HTTPS dat mijn app veilig is?

Nee. HTTPS versleutelt de verbinding tussen de browser en je server, maar het voorkomt niet dat de server data aan de verkeerde mensen geeft. Een tabel zonder RLS of een key in de frontend blijven blootliggen met een groen slotje.

Is het scannen van mijn site gevaarlijk ervoor?

Nee. Scanaris voert read-only checks uit op informatie die al openbaar is (headers, cookies, certificaten, toegankelijke bestanden). Het wijzigt, verwijdert of injecteert nooit iets. Scan alleen sites die van jou zijn of die je mag testen.

Wat is het verschil met een pentest?

Een pentest is dagen handwerk van specialisten die met toestemming proberen je app uit te buiten. Scanaris is een automatische, read-only controle in seconden op de configuratiefouten die in AI-gebouwde apps terugkomen. Het dekt wat het vaakst misgaat; het vervangt geen pentest als je heel gevoelige data verwerkt.

Moet ik verstand hebben van security om het te gebruiken?

Nee. Elke bevinding komt met een duidelijke uitleg en een kant-en-klare fixprompt voor je AI, zodat je het kunt oplossen zonder expert te zijn. Plak de prompt, bekijk de wijziging en scan opnieuw om te bevestigen dat het dicht is.

Hoe vaak moet ik opnieuw scannen?

Elke keer dat de AI de database aanraakt of een integratie toevoegt, en minstens wekelijks als de app in productie staat. Betaalde plannen doen het vanzelf en waarschuwen je alleen als er iets verandert.