← Stack-gidsen

Bolt

Beveiliging voor je Bolt-app

Bolt.new zet in je browser een complete full-stack app op en publiceert die met één klik, maar achter die snelheid schuilen beveiligingskeuzes: keys die je in de chat plakt en in de frontend belanden, nul headers, en een SPA die AI niet ziet. Scan je app en weet het zeker in 30 seconden.

Typische risico's bij Bolt

Keys die je in de chat plakt en in de frontend belanden

Om een service te "koppelen" plak je de key in Bolt, en vaak wordt die in de clientcode verwerkt. Alles wat de browser bereikt is openbaar: die key ligt te kijk.

Publiceren zonder beveiligingsheaders

De deploy van Bolt serveert je app via HTTPS, maar zonder CSP, HSTS of X-Frame-Options. Die headers staan er niet standaard en moet je toevoegen bij je host.

SPA die onzichtbaar is voor zoekmachines en AI

Bolt genereert SPA's waarvan de inhoud pas verschijnt nadat JavaScript draait. AI-crawlers en sommige zoekmachines zien een vrijwel lege pagina en kunnen je niet indexeren of citeren.

"Bolt-beveiliging" zijn twee dingen die je beter uit elkaar houdt. Het eerste is het Bolt-project zelf: wat je in de chat plakt, waar omgevingsvariabelen belanden, met wie je het project deelt en waar het gedeployed wordt. Het tweede is de code die Bolt genereert, bijna altijd een Vite- en React-SPA gekoppeld aan Supabase, die dezelfde vijf of zes fouten als altijd meebrengt. Deze gids behandelt beide, met een instructiebestand zodat Bolt de regels bij elke generatie toepast, een reviewprompt vóór het deployen en de manier om het resultaat van buitenaf te controleren.

Deel 1: je Bolt-project beveiligen

Bolt draait in je browser, en dat verandert waar sleutels leven

Bolt voert het hele project in de browser uit met WebContainers. Er is geen eigen server terwijl je ontwikkelt, dus elke sleutel die je in een .env-bestand zet, zit in het project, en met het voorvoegsel VITE_ belandt hij ingebed in de bundle die gedeployed wordt. Voor de browser is dat alleen in orde met publieke sleutels (de anon van Supabase, de publishable van Stripe). Geheime horen niet in de frontend: die gaan in een Supabase Edge Function of een Netlify-functie, en de client roept de functie aan.

Wat je in de chat plakt, belandt in de code

Als je een sleutel in het gesprek plakt om een dienst te "koppelen", schrijft het model hem waar hij nodig is, meestal in een component. Van daaruit reist hij naar elke bezoeker. De regel: publieke sleutels gaan in .env; geheime in het dashboard van de dienst die de functie uitvoert (Supabase of Netlify), nooit in de chat. En heb je er per ongeluk een geplakt, roteer hem dan: de geschiedenis bewaart hem, ook als je hem uit de code verwijdert.

Delen, exporteren en deployen

Een gedeelde projectlink toont de volledige code, inclusief de .env. Controleer vóór het delen wat erin staat. Exporteer je naar GitHub, maak de repository dan privé en houd .env in .gitignore. De deploy met één klik gaat naar Netlify, op een .netlify.app-subdomein of je eigen domein; Netlify geeft TLS en de redirect naar HTTPS, maar geen beveiligingsheaders, en de productie-omgevingsvariabelen configureer je in zijn dashboard, niet in de .env van het project.

Supabase is van jou

De Supabase-integratie maakt tabellen, policies en functies aan vanuit de chat. RLS, buckets en Auth-instellingen blijven jouw verantwoordelijkheid, en Bolt maakt dezelfde fouten als Lovable met policies. De beveiligingsgids voor Supabase heeft drie SQL-query's om het project in een minuut door te lichten.

Deel 2: de fouten die Bolt in je code stopt

  • Geheime sleutels in componenten. Een aanroep naar OpenAI of Stripe vanuit React met de sleutel erin. Zoek naar sk_, service_role en Bearer in je gedeployde bundle.
  • Tabellen zonder RLS of met using (true)-policies. Als een query faalt op rechten, zet het model de policy open in plaats van hem te repareren.
  • Edge Functions zonder authenticatie. Het template antwoordt iedereen met Access-Control-Allow-Origin: *; als de functie geld uitgeeft, kan iedereen het uitgeven.
  • Controles alleen in de interface. De beheerpagina wordt verborgen als je geen admin bent, maar de API antwoordt toch.
  • Source maps in productie. Sommige Vite-templates bevatten build.sourcemap: true; dat publiceert je originele code naast de geminificeerde.
  • Onzichtbaar voor AI en zoekmachines. De HTML komt leeg aan en de bots van ChatGPT of Perplexity zien niets. Als de landingspagina ertoe doet, prerender hem of scheid hem af. Details in waarom met AI gebouwde apps onzichtbaar zijn voor AI.
  • Geen beveiligingsheaders. Geen CSP, geen HSTS, geen X-Frame-Options. Op Netlify voeg je ze toe met een bestand.

Een blok zodat Bolt het altijd toepast

Bolt leest het bestand .bolt/prompt van je project als permanente instructies. Maak het bestand aan en plak dit:

Beveiligingsregels van dit project:
- Zet nooit geheime sleutels in frontendcode of in VITE_-variabelen. Integraties met geheime sleutels gaan in een Supabase Edge Function of een Netlify-functie die de sleutel uit zijn omgevingsvariabelen leest.
- Elke nieuwe tabel heeft RLS ingeschakeld en één expliciete policy per bewerking. Gebruik geen using (true) of with check (true), behalve op publieke alleen-lezen inhoud, en zeg het me als je dat doet.
- Elke serverfunctie verifieert het JWT van de gebruiker, beperkt Access-Control-Allow-Origin tot ons domein en valideert de invoer.
- Adminrechten worden gecontroleerd in RLS of op de server, nooit alleen in de interface.
- Gebruikersbestanden gaan naar private buckets met policies en ondertekende URL's.
- build.sourcemap blijft uitgeschakeld in productie.
- Zet een echte titel, metabeschrijving en Open Graph-tags in index.html.

En de headers voor Netlify, in een bestand public/_headers dat met de app meegedeployed wordt:

/*
  Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
  X-Content-Type-Options: nosniff
  X-Frame-Options: DENY
  Referrer-Policy: strict-origin-when-cross-origin
  Permissions-Policy: camera=(), microphone=(), geolocation=()

Een reviewprompt vóór het deployen

Beoordeel de beveiliging van dit project en verander nog niets. Antwoord met een lijst:
1. Sleutels of tokens in frontendcode of in VITE_-variabelen, met bestand en regel.
2. Tabellen zonder RLS en policies met using (true) of with check (true).
3. Serverfuncties die het JWT niet verifiëren, antwoorden met Access-Control-Allow-Origin: * of de invoer niet valideren.
4. Adminacties die alleen in de interface beschermd zijn.
5. Publieke buckets met gebruikersbestanden.
6. Ingeschakelde build.sourcemap en een ontbrekend _headers-bestand.
7. Metadata in index.html die nog van het template zijn.
Stel voor elk punt de exacte wijziging voor en wacht op mijn bevestiging.

Controleer het resultaat van buitenaf

curl -sI https://jouwdomein.com | grep -iE "strict-transport|x-frame|x-content-type"
curl -s https://jouwdomein.com | grep -oE '/assets/[^"]+\.js' | head -1
curl -s https://jouwdomein.com/assets/index-XXXX.js | grep -oE "sk_live_[A-Za-z0-9]+|service_role|eyJhbGciOi[A-Za-z0-9_-]{20,}" | head -3
curl -sI https://jouwdomein.com/assets/index-XXXX.js.map | head -1

Verwacht: headers aanwezig, geen sleutel in de chunk en een 404 op de source map. Test daarna de Supabase-API zonder sessie zoals de Supabase-gids uitlegt. Scanaris voert deze controles en zo'n veertig andere uit in 30 seconden: het detecteert dat de app met Bolt is gebouwd, zoekt geheimen in de bundle, controleert source maps, headers, CORS, cookies en blootgestelde bestanden, test de Supabase-tabellen zonder sessie en kijkt of je inhoud leesbaar is zonder JavaScript. Voor elke fout krijg je een fix prompt om in de Bolt-chat te plakken, en het kan elke dag opnieuw scannen, want een hergeneratie kan een opgeloste fout terugbrengen.

Veelgestelde vragen

Zijn VITE_-variabelen privé?

Nee. Vite bakt alles wat met VITE_ begint in de bundle; het is zijn manier om te zeggen "dit is publiek". Geheime sleutels dragen dat voorvoegsel niet en worden niet vanuit de frontend gelezen.

Kan ik beveiligingsheaders zetten op Netlify?

Ja, met het bestand _headers in de publieke map of met netlify.toml. Netlify past ze toe bij de volgende deploy; controleer het met curl.

Wat doe ik als ik een geheime sleutel in de Bolt-chat heb geplakt?

Roteer hem bij de brondienst, ook als je hem uit de code hebt verwijderd: de projectgeschiedenis en eerdere deployments bewaren hem. Voeg hem daarna toe in het Supabase- of Netlify-dashboard en vraag Bolt de integratie naar een functie te verplaatsen.

Gerelateerde checks

Doorstaat je Bolt-app deze checks?

Scan hem gratis en krijg een fixprompt voor elk probleem dat we vinden.

Mijn site gratis scannen