Gå til indhold

Sikkerhed i AI-genereret kode: 12 typiske huller og hvordan du finder dem

Sikkerhed i AI-genereret kode: 12 typiske huller i apps fra Lovable, Bolt og Cursor forklaret uden fagsprog, og hvordan du selv tjekker for dem.

Af

Freelance full-stack udvikler

Udgivet
Læsetid
13 min.
Indhold i indlægget9

Sikkerhed i AI-genereret kode handler sjældent om avanceret hacking. De typiske huller er hemmelige nøgler der ligger synligt i browseren, data som alle kan læse, og en server der tror på alt hvad den får tilsendt. Du kommer derfor langt ved at tjekke de samme 12 steder, og flere af dem kan du tjekke selv uden at kunne kode.

Listen er skrevet til dig der har bygget en app med Lovable, Bolt, v0, Replit eller Cursor og nu vil lukke rigtige brugere ind. Jeg er freelanceudvikler og hjælper selv med at gennemgå og vedligeholde den slags apps, så læs mine råd om hvornår du skal have hjælp med det forbehold.

Den korte version

Her er de 12 huller samlet. Alvor er min vurdering af hvor stor skaden typisk bliver hvis appen har rigtige brugere og rigtige data.

De 12 typiske sikkerhedshuller i AI-genereret kode
Hvad kan gå galtAlvorKan du selv tjekke det?
1. API-nøgler i browserenFremmede bruger dine betalte konti, fx hos OpenAIHøjJa
2. Databasens adminnøgle i browserenAlle kan læse, ændre og slette altKritiskJa
3. Nøgler i Git-historikkenSlettede nøgler kan stadig misbrugesHøjDelvist
4. Åben databaseAlle data kan hentes uden loginKritiskDelvist
5. Beskyttelse kun i brugerfladenAdminfunktioner kan kaldes direkteKritiskJa
6. Andres data et id vækKunder ser hinandens fakturaer og profilerKritiskJa
7. Brugere giver sig selv rettighederGratis brugere bliver admin eller får betalt adgangHøjNej
8. Serveren stoler på browserenPriser, rabatter og betalingsstatus kan ændresHøjDelvist
9. Brugerindhold der kører som kodeFremmed kode kører i dine brugeres browserHøjNej
10. Filuploads uden grænserPrivate filer bliver offentligeMellemDelvist
11. Opdigtede eller forældede pakkerOndsindet kode kommer ind via afhængighederHøjNej
12. Ingen bremser på misbrugStore regninger og overtagne kontiMellemNej

Min tommelfingerregel er at starte med de huller der er markeret som kritiske. De handler alle om adgang til data, og det er dem der gør mest skade. Er du ved at gøre en prototype klar til lancering, dækker min guide til at få en AI-bygget app i produktion hele vejen fra prototype til drift. Denne liste er sikkerhedsdelen af den.

Nøgler og hemmeligheder

En API-nøgle er et kodeord som din app bruger til at tale med andre tjenester, fx OpenAI, Stripe eller din database. Problemet opstår når nøglen havner et sted hvor andre kan se den.

1. Hemmelige API-nøgler i browseren

AI-værktøjer vælger ofte den korteste vej til noget der virker. Skal appen kalde OpenAI, bliver kaldet tit lagt direkte i frontend-koden, altså den del der kører i brugerens browser. Det virker med det samme, men nøglen sendes med til alle besøgende. Alle med lidt tålmodighed kan kopiere den og bruge din konto på din regning.

Sådan tjekker du: åbn din udgivne app, højreklik og åbn browserens udviklerværktøjer. Søg i kildekoden efter sk- (typisk starten på OpenAI-nøgler) og sk_live (Stripes hemmelige nøgler). En nøgle der starter med pk_, er Stripes offentlige nøgle og må gerne ligge der.

Løsningen er at flytte kaldet til serveren eller en edge function (et lille stykke kode der kører på serveren), lave en ny nøgle og sætte et forbrugsloft hos udbyderen.

2. Databasens adminnøgle i browseren

Supabase og Firebase har to slags adgang: en offentlig nøgle der må ligge i browseren, og en hemmelig adminnøgle der går uden om alle regler. Supabase skriver selv i sin dokumentation om API-nøgler at den offentlige nøgle kun når det som reglerne tillader, og at den hemmelige aldrig må ligge i en browser, en udgivet app eller i versionsstyring.

Når en AI-assistent rammer en fejl som "permission denied", kan en hurtig løsning være at skifte til adminnøglen. Så forsvinder fejlen, og det gør beskyttelsen også.

Sådan tjekker du: søg på samme måde efter sb_secret_ (Supabases nye hemmelige nøgler) og service_role (navnet på den ældre adminnøgle) i kildekoden. Finder du adminnøglen, skal du gå ud fra at alle data kan have været tilgængelige indtil det modsatte er vist.

3. Nøgler i Git-historikken

Git er det system der gemmer alle versioner af din kode, og et repository (kodearkiv) husker alt. Er en .env-fil med nøgler blevet gemt bare én gang, ligger nøglerne i historikken også selvom filen er slettet senere. Er kodearkivet offentligt på GitHub, eller har du delt det med andre, skal du regne nøglerne for lækkede.

Løsningen er ikke at slette filen. Du skal lave nye nøgler hos hver udbyder og slå de gamle fra. Bagefter skal .env stå i .gitignore så det ikke sker igen. GitHub kan advare om kendte nøgletyper, men regn ikke med at advarslerne fanger alt.

Adgangskontrol: hvem må se og ændre hvad

Adgangskontrol er de regler der afgør hvilke data hver bruger må se og ændre. Fejl i adgangskontrollen (broken access control) er nummer ét på OWASP Top 10, standardlisten over de mest kritiske risici i webapps. Det er også her jeg selv starter når jeg skal vurdere om en app er klar til rigtige brugere.

4. Databasen er åben for alle

Apps bygget på Supabase taler ofte direkte med databasen fra browseren. Det er kun sikkert hvis hver tabel har regler for rækkeadgang (Row Level Security, RLS) som fx siger at en bruger kun må læse sine egne rækker. Mangler reglerne, eller tillader de alt, kan alle med den offentlige nøgle hente hele tabellen.

Det er sket i praksis. I 2025 fandt sikkerhedsforskere 303 sårbare endepunkter fordelt på 170 af 1.645 undersøgte Lovable-projekter, med e-mails, telefonnumre, betalingsoplysninger og API-nøgler (forskernes redegørelse for CVE-2025-48757). Ifølge dem tjekkede Lovables egen sikkerhedsscanner dengang kun om reglerne fandtes, ikke om de virkede. De Supabase-specifikke tjek har jeg samlet i tjeklisten til Lovable og Supabase før lancering.

5. Beskyttelsen sidder kun i brugerfladen

Et klassisk mønster: adminsiden vises kun for admins, og knappen "Slet bruger" er skjult for alle andre. Men serveren eller databasen bag knappen tjekker ikke hvem der spørger. AI-værktøjer bygger det du beder om at se, og det du ser, er brugerfladen.

Sådan tjekker du: log ind som almindelig bruger og skriv adressen på adminsiden direkte i adresselinjen. Kommer du ind, har du et hul. Kommer du ikke ind, beviser det desværre ikke noget, for siden kan være låst mens kaldene bagved stadig svarer.

6. Andres data ligger et id væk

Mange apps viser data via adresser som /faktura/1043. Tjekker serveren kun at du er logget ind, og ikke at faktura 1043 er din, kan du se faktura 1042 ved at ændre tallet. Fejltypen hedder IDOR (insecure direct object reference), og den er en typisk måde kundedata lækker på.

Sådan tjekker du: opret to testbrugere, A og B. Opret noget som A, kopiér adressen, log ind som B og åbn adressen. B må ikke kunne se noget.

7. Brugere kan give sig selv flere rettigheder

Det her hul er svært at se uden at læse koden. Ofte gemmes brugerens rolle eller abonnement i samme tabel som navn og profilbillede, fx i et felt som role eller plan. Må brugere opdatere deres egen række, må de som regel også opdatere de felter. Så kan en bruger gøre sig selv til admin eller give sig selv betalt adgang med ét kald fra browseren.

Løsningen er at rolle og abonnement kun kan ændres af serveren og aldrig af brugeren selv. Serveren ændrer dem fx når betalingsudbyderen bekræfter en betaling.

Data ind og ud

Alt hvad brugeren sender til din app, og alt hvad appen viser til andre, er en mulig vej ind. De næste tre huller handler om at serveren skal tjekke selv.

8. Serveren stoler på det browseren sender

Alt hvad der kommer fra browseren, kan brugeren ændre. Validering i formularen (fx at et felt skal være en e-mail) hjælper brugeren, men beskytter ikke noget. AI-genererede apps sender ofte pris, rabat eller antal fra browseren til betalingen, og serveren bruger tallene uden at tjekke dem.

Betalingsstatus er det farligste eksempel. Markerer appen en ordre som betalt fordi brugeren lander på en "tak for dit køb"-side, kan man springe betalingen over. Betaling skal bekræftes via en webhook (en besked fra fx Stripe direkte til din server), og serveren skal tjekke beskedens signatur så ingen andre kan sende en falsk.

9. Brugerindhold der kører som kode

Når brugere skriver tekst som andre ser, fx kommentarer, profilnavne eller beskeder, skal teksten vises som tekst. Indsættes den i stedet som HTML, kan en bruger skrive et script der kører i andre brugeres browser og fx stjæler deres login. Det kaldes cross-site scripting (XSS). I Veracodes test af over 100 sprogmodeller undlod den genererede kode at beskytte mod XSS i 86 % af de relevante kodeprøver (Veracodes GenAI Code Security Report 2025).

Samme familie af fejl findes i databasekald hvor brugerens tekst sættes direkte ind (SQL injection), og i AI-funktioner hvor brugerens tekst kan give modellen nye instrukser (prompt injection). Det sidste er vigtigt hvis din AI-funktion kan slå op i data eller udføre handlinger.

10. Filuploads uden grænser

Upload af billeder og dokumenter bliver ofte sat op med en offentlig lagerplads (en bucket) fordi det er nemmest at få til at virke. Så kan alle med linket se filerne. Det er fint til profilbilleder, men ikke til kontrakter, lønsedler eller billeder af ID-kort.

Tjek også om der er grænser for filtype og størrelse. Uden dem kan fremmede bruge din lagerplads på din regning eller uploade filtyper der kan indeholde kode, fx SVG og HTML.

Sådan tjekker du: upload en fil som testbruger, kopiér linket og åbn det i et privat browservindue uden login.

Drift og misbrug

De sidste to huller opstår ikke i en enkelt funktion. De handler om hvad der sker omkring appen når den først kører.

11. Opdigtede eller forældede pakker

Moderne apps bygger på hundredvis af færdige kodepakker. AI-modeller finder nogle gange på pakkenavne der lyder rigtige. I et studie fra USENIX Security 2025 var gennemsnitligt mindst 5,2 % af de foreslåede pakker fra kommercielle modeller opdigtede, og 21,7 % fra open source-modeller (studiet om opdigtede pakker). Det åbner for at angribere kan registrere de opdigtede navne med ondsindet kode, et angreb der er blevet kaldt slopsquatting.

Den mere kedelige udgave er forældede pakker med kendte huller. AI-værktøjet kan vælge en version det kender fra sine træningsdata, og ingen opdaterer bagefter. Løsningen er automatiske advarsler, fx Dependabot på GitHub eller npm audit, og en fast rytme for opdateringer som en del af almindelig drift og vedligehold af webapps.

12. Ingen bremser på misbrug

Begrænsning af hvor mange forsøg man får (rate limiting) kommer typisk ikke med medmindre du beder om det. Uden den kan en bot gætte adgangskoder uden at blive stoppet, oprette tusindvis af konti eller kalde din AI-funktion igen og igen. Regningen for modellens forbrug lander hos dig.

Hertil kommer fejlbeskeder der viser for meget. En fejlside med tekniske detaljer om databasen eller serveren giver en angriber et kort over systemet. Brugeren skal se en venlig besked, og detaljerne skal i en log som kun du kan se.

Hvorfor AI-værktøjer laver de samme fejl igen og igen

Værktøjerne er gode til at lave noget der virker. Sikkerhed er netop det du ikke kan se når noget virker: en app med en åben database ser præcis ud som en app med en lukket. Jeg ser tre grunde til at hullerne går igen:

  • Værktøjet prøver at få fejlen til at forsvinde. Når en regel blokerer et kald, er den hurtigste løsning at fjerne eller løsne reglen.
  • Din prompt nævner sjældent sikkerhed. Du beder om "en side hvor kunder kan se deres fakturaer", ikke om "kun deres egne fakturaer".
  • Hver ny prompt kan ændre gammel kode. En regel der var rigtig i går, kan være skrevet om i dag uden at du får det at vide.

Den samme Veracode-rapport fandt at 45 % af kodeprøverne fra over 100 modeller indeholdt sårbarheder fra OWASP Top 10, og at nyere og større modeller ikke klarede sig bedre på sikkerhed. Du kan altså ikke vente dig ud af problemet ved at skifte til næste model.

Kan du genkende mønsteret hvor hver rettelse skaber en ny fejl, er det et af tegnene på at det er tid til at stoppe med at prompte og hyre en udvikler.

Hvornår du kan klare det selv, og hvornår du skal have hjælp

Ikke alle AI-byggede apps har brug for en udvikler med det samme. Min ærlige vurdering ser sådan ud:

  • Du kan klare det selv hvis appen er en prototype med testdata, en intern demo eller et værktøj uden persondata og betaling. Brug listen som huskeliste, tjek hul 1, 2, 5 og 6 selv, og spar pengene til senere.
  • Du bør have en udvikler til at kigge når der kommer rigtige brugere, persondata eller betaling ind i appen. Det gælder også hvis en erhvervskunde begynder at stille spørgsmål om sikkerhed.
  • Du skal have en anden end mig hvis du har brug for en formel penetrationstest eller dokumentation til en certificering eller et kundekrav. Det er et job for et specialiseret sikkerhedsfirma. En udvikler som mig kan lukke hullerne og gøre koden klar, men den uafhængige test bør laves af nogen der ikke har skrevet koden.

Finder du mange af hullerne på én gang, er det værd at overveje om appen skal repareres eller bygges om. Det har jeg skrevet en beslutningsguide om at genskrive eller redde en AI-app til.

Næste skridt: luk hullerne i den rigtige rækkefølge

Rækkefølgen betyder noget. Nogle huller er åbne døre lige nu, andre kræver en målrettet indsats at udnytte.

  1. Lav nye nøgler for alt der har ligget i browseren eller i Git, og sæt forbrugslofter hos dine udbydere.
  2. Tjek adgangskontrollen med to testbrugere (hul 4-7), og luk de kritiske fund først.
  3. Flyt priser, roller og betalingsstatus til serveren (hul 7 og 8).
  4. Slå automatiske advarsler til for pakker og fejl.
  5. Gentag tjekket efter hver større ændring fordi AI-værktøjet kan skrive dine regler om uden at sige det.

Vil du have en udvikler til at gennemgå koden og holde den sikker efter lancering, kan du læse om vedligehold og videreudvikling af eksisterende apps. Er appen stadig en prototype der skal gøres klar til rigtige brugere, passer forløbet fra AI-app til produktion bedre.

Ofte stillede spørgsmål

Kan jeg ikke bare bede AI-værktøjet om at gøre appen sikker?

Delvist. Værktøjet kan godt rette et konkret hul hvis du beskriver det præcist, fx at en bestemt tabel skal have regler så brugere kun ser egne rækker. Det kan bare ikke bevise at rettelsen virker, og det melder ofte at alt er i orden selvom det ikke er det. Brug AI til at rette, og test bagefter selv med to brugere, eller få en udvikler til at verificere.

Skal jeg anmelde det hvis jeg finder et sikkerhedshul?

Det afhænger af om persondata faktisk har været tilgængelige for uvedkommende. Er det tilfældet, skal et brud på persondatasikkerheden som udgangspunkt anmeldes til Datatilsynet uden unødig forsinkelse og om muligt inden for 72 timer (GDPR artikel 33) medmindre det sandsynligvis ikke indebærer en risiko for de berørte. Kan du ikke udelukke at data er hentet, så dokumentér hvad du ved. Det her er ikke juridisk rådgivning, så tal med en rådgiver ved tvivl.

Er min app for lille til at være et mål?

Nej. Angreb på små apps er typisk ikke målrettede. Automatiske programmer gennemsøger internettet efter lækkede nøgler, åbne databaser og loginsider uden begrænsninger, og de er ligeglade med om du har 20 eller 20.000 brugere. En åben database med få brugere er stadig et alvorligt problem hvis der ligger persondata i den.

Hvor ofte skal jeg tjekke sikkerheden igen?

Efter hver større ændring og som minimum en gang om måneden for pakkeopdateringer. AI-værktøjer kan omskrive eksisterende kode når du beder om noget nyt. En regel der virkede ved lancering, kan derfor være ændret uden at du har bemærket det. Automatiske advarsler fra fx Dependabot og Supabases Security Advisor fanger en del, men ikke fejl i selve reglernes logik.