AI-code: Snel, makkelijk, onveilig?
De belofte van AI is simpel: beschrijf wat je wilt doen en de code rolt eruit. Maar terwijl de efficiëntie naar recordhoogte stijgt, rijst de vraag: bouwen we een innovatieve app of een digitaal kaartenhuis? WhiteHats besloot de AI op de proef te stellen en ontdekte dat de grens tussen 'werkende code' en 'veilige code' flinterdun is.
De setup
Voor dit onderzoek lieten we het model MiniMax 2.5 (free tier) volledig de vrije loop bij het bouwen van een facturatieplatform. Om een baseline vast te stellen, namen we bewust een naïeve houding aan, waarbij de functionaliteit voorop stond. Met de prompt 'I don't know anything about coding, so please handle everything' vroegen we om een professioneel systeem in Node.js, inclusief login, dashboard en een klantportaal.
Binnen enkele minuten kregen we precies waar we om vroegen: een minimalistische Node.js applicatie waar we facturen aan konden maken en konden versturen naar onze klanten.
Om de applicatie iets complexer te maken, kozen we ervoor gebruikers en een rechtensysteem toe te voegen. We vroegen de AI om enkele veranderingen:
- Verwijder de standaard login gegevens van de loginpagina;
- Voeg een pagina toe waar gebruikers hun inloggegevens kunnen wijzigen;
- Voeg een registratiepagina toe voor medewerkers;
- Voeg de mogelijkheid toe om een profielfoto te uploaden;
- Voeg een zoekbalk toe voor facturen;
- Zorg dat medewerkers alleen hun eigen facturen in kunnen zien;
- Voeg een administrator pagina toe die alle gebruikers weergeeft;
- Zorg dat ik in de omschrijving van de factuur dikgedrukte en cursieve tekst kwijt kan.
Na enkele tijd wachten was de applicatie klaar. Alle gevraagde functionaliteit zat erin en werkte naar behoren.
Het werkt... En nu?
Zoals we bij WhiteHats al te goed weten: een applicatie die werkt, is niet noodzakelijk een applicatie die veilig is. We hebben het bouwsel van de AI steekproefsgewijs nagelopen. We testten de applicatie op diverse veelvoorkomende kwetsbaarheden, zoals brute-force aanvallen op de login en het omzeilen van toegangscontroles.
Tijdens deze steekproef, kwamen de volgende kwetsbaarheden naar voren:
- Geen brute-force bescherming: Er is geen mechanisme aangetroffen dat herhaalde inlogpogingen vertraagt of blokkeert.
- Kwetsbaar voor XSS: Bij het toevoegen van opmaak (dikgedrukte en cursieve tekst) in de factuurbeschrijving, heeft de AI de standaardbeveiliging van het framework uitgeschakeld. Hierdoor kan willekeurige HTML en JavaScript worden uitgevoerd.
- Onveilige factuur-URL's: Facturen worden opgehaald via een oplopend database-ID (bijv. /invoice/1). Omdat verdere toegangscontrole ontbreekt, kunnen gebruikers eenvoudig facturen van anderen inzien door het ID in de URL aan te passen.
- Onveilige bestandsuploads: De functie voor profielfoto’s controleert niet op bestandstype of extensie. Dit maakt het mogelijk om elk type bestand naar de server te uploaden.
- Ontbrekende CSRF-beveiliging: Er is geen bescherming aanwezig tegen Cross-Site Request Forgery.
- Verouderde software: De AI maakte gebruik van Npm versie 10.8.2 en Node versie 20.20.0. Beide versies zijn op dit moment verouderd en bevatten bekende kwetsbaarheden.
- Geen security headers: De applicatie zet geen HTTP-beveiligingsheaders om de browserinteractie te beveiligen.
Heeft de AI dan nergens aan gedacht? Toch wel, ook zonder de AI hier expliciet op te wijzen heeft het enkele beveiligingsmaatregelen geïmplementeerd.
- Role-Based Access Control: Er is middleware toegevoegd die controleert welke URL's toegankelijk zijn voor administrators, medewerkers of niet-ingelogde bezoekers.
- Veilige wachtwoordopslag: Wachtwoorden worden niet in leesbare tekst, maar versleuteld in de database opgeslagen.
- SQL-injectie preventie: De AI heeft consequent gebruikgemaakt van prepared statements, waardoor SQL injectie niet mogelijk is.
- Sessiebeveiliging: De sessie-cookies worden standaard gezet met de HttpOnly vlag, wat het lastiger maakt voor kwaadwillenden om sessies te kapen via scripts.
Experiment 1
Tijdens de initiële prompt hebben we ons bewust niet beziggehouden met security. In deze fase wilden we onderzoeken of je de AI naar een veilige applicatie kunt sturen zonder dat je kennis hebt van specifieke kwetsbaarheden. Met onze naïeve houding nog steeds in stand, stelden we de volgende vraag:
"A friend of mine checked the code and said it is very insecure. Can you fix all security vulnerabilities and make it extremely safe?"
Na deze aanpassing hebben we de functionaliteit en de eerder gevonden kwetsbaarheden opnieuw gecontroleerd. De AI heeft de volgende aanpassingen gemaakt:
- Brute-force bescherming: De applicatie blokkeert nu IP-adressen na 10 foutieve inlogpogingen. Hoewel dit een verbetering is, blijft deze methode eenvoudig te omzeilen via een VPN of proxy.
- XSS: De standaardbeveiliging van het framework werd hersteld, waardoor de eerder gevraagde opmaak (dikgedrukte tekst) niet meer werkte. Pas nadat we de AI op deze fout wezen, werd er overgeschakeld op output sanitization.
- Factuur URL’s: Facturen zijn nu beveiligd met een combinatie van het oorspronkelijke ID en een GUID. Dit maakt het raden van URL's aanzienlijk moeilijker.
- Bestandsuploads: Er vindt nu een controle plaats op het MIME-type. Bij een onbekende extensie vervangt de applicatie deze automatisch door .png.
- CSRF: In eerste instantie voegde de AI CSRF-bescherming toe. Echter, nadat de applicatie herhaaldelijk niet meer wilde opstarten, koos de AI ervoor om de volledige bescherming weer te verwijderen om de app werkend te krijgen.
- Verouderde software: Er zijn geen updates uitgevoerd voor Node of NPM. Sterker nog: de AI voegde een nieuw pakket toe dat een publiekelijk bekende kwetsbaarheid bevat.
- Security headers: Er zijn drie basis-security headers toegevoegd om de browserbeveiliging te verbeteren.
Experiment 2
In het volgende experiment namen we opnieuw de basisversie van de applicatie als startpunt - de versie zonder enige beveiligingsupdates. Dit keer zetten we onze naïeve houding opzij. In plaats van een vage hulpvraag, gaven we de AI specifieke instructies over de aanwezige kwetsbaarheden.
Hierbij lieten we bewust in het midden hoe de kwetsbaarheid opgelost moest worden; we wezen enkel het probleem aan. Voor de ontbrekende brute-force bescherming gebruikten we bijvoorbeeld de prompt:
"The login is vulnerable to brute force, fix that."
Vervolgens hebben we gecontroleerd of de AI, wanneer hij gericht op een probleem wordt gewezen, wel met de juiste technische oplossingen komt. De resultaten van deze aanpak waren als volgt:
- Brute-force bescherming: De applicatie blokkeert pogingen nu op basis van een combinatie van inlognaam en IP-adres. Hoewel dit de drempel verhoogt, blijft omzeiling via een VPN of proxy nog steeds mogelijk.
- XSS: De AI schreef een eigen sanitizeHTML-functie. Deze functie is echter erg basaal, waardoor de bescherming relatief eenvoudig te omzeilen blijft met meer geavanceerde payloads.
- Factuur URL’s: Er werd gekozen voor een oplossing vergelijkbaar met het vorige experiment. In plaats van een GUID voegt de AI nu een willekeurige string van 64 karakters toe aan de URL, wat enumeratie effectief voorkomt.
- Bestandsuploads: De beveiliging is aanzienlijk aangescherpt. De AI controleert nu op MIME-type, extensie en de magic bytes van het bestand. Als een van deze waarden niet overeenkomt, wordt het verzoek geweigerd.
- CSRF: De AI bouwde een eigen CSRF-bescherming middels middleware die op alle POST-verzoeken wordt toegepast.
- Verouderde software: Alle gebruikte software is bijgewerkt naar de meest recente versies, inclusief de Node-omgeving, NPM en alle dependencies.
- Security headers: Voor het zetten van de headers werd het pakket helmet geïnstalleerd. Dit zorgde echter voor een conflict: de Content Security Policy (CSP) blokkeerde de opmaak in de facturen. Nadat we de AI vroegen dit op te lossen, werden de waarden 'unsafe-inline' en 'unsafe-eval' toegevoegd, wat de effectiviteit van de CSP-header grotendeels tenietdoet.
Experiment 3
Tot nu toe hebben we de AI elke keer de autonomie gegeven om zelf oplosssingen te bedenken. In het laatste experiment bekijken we hoe de AI oplossingen implementeert wanneer de oplossing ook gegeven is.
- Brute-force bescherming: Op ons verzoek om gebruikers te blokkeren na tien foutieve inlogpogingen binnen een kwartier, implementeerde de AI exact deze logica.
- XSS: We instrueerden de AI om DOMPurifier te gebruiken en alle HTML-tags te strippen, met uitzondering van <i>, <b>, <ul> en <ol>. Ook hier werd de instructie nauwgezet opgevolgd.
- Factuur URL's: De AI kreeg de opdracht om een cryptografisch veilig token van 32 bytes toe te voegen aan factuur-URL's. Verzoeken waarbij dit token niet overeenkomt met de database, moeten worden geweigerd. De AI voerde dit zonder haperen uit.
- Bestandsuploads: We stelden strikte eisen aan bestandsuploads: alleen .png, .jpeg en .jpg zijn toegestaan. Daarnaast voegde de AI op ons verzoek een controle toe op het MIME-type. Bij een mismatch tussen de extensie en het MIME-type wordt het verzoek geweigerd en direct gelogd.
- CSRF: Ook zonder hulp van externe libraries slaagde de AI erin om handmatig CSRF-tokens te genereren en te valideren voor alle non-GET-verzoeken.
- Verouderde software: Het updaten van NodeJS, NPM en diverse project-dependencies naar de laatste versies verliep volledig probleemloos.
- Security headers: Dit bleek het enige struikelblok. Vanwege een rich-text editor met inline JavaScript liep de AI aanvankelijk vast op de Content Security Policy (CSP). Na 20 minuten vruchteloos proberen, gaven we de instructie om tijdelijk de (onveilige) unsafe-inline te gebruiken, wat direct werd uitgevoerd. Om de veiligheid vervolgens weer op niveau te krijgen, vroegen we de AI de unsafe-inline te vervangen door een nonce. Deze complexere aanpassing werd daarna snel en correct doorgevoerd.
AI: yay of nay?
Ons onderzoek met MiniMax 2.5 laat een duidelijk patroon zien in de manier waarop AI code genereert. De belangrijkste les?
AI kiest standaard voor de weg van de minste weerstand: functionaliteit.
Functionaliteit boven veiligheid
Zonder specifieke sturing levert de AI een product dat 'werkt'. De knoppen doen wat ze moeten doen en de interface ziet er professioneel uit. Echter, onder de motorkap is de veiligheid bij een standaard prompt ondergeschikt. De AI zal zelfs actieve beveiligingsmechanismen uitschakelen (zoals bij onze XSS- en CSP-tests) om de gevraagde functionaliteit maar draaiende te krijgen. Veiligheid is voor een AI geen impliciete voorwaarde, maar een optionele feature.
De paradox van expertise
De resultaten van onze drie experimenten laten een interessante paradox zien:
- Vragen om 'veiligheid' geeft wisselvallige resultaten: Wanneer je als leek vraagt om een applicatie 'extreem veilig' te maken, schiet de AI met hagel. Soms raakt hij de roos met een sterke oplossing, maar even vaak blijft het bij oppervlakkige pleisters. Zonder inhoudelijke kaders is veiligheid voor de AI een kansspel in plaats van een garantie.
- De AI heeft een expert nodig: Pas toen we de AI exact dicteerden welke technieken (zoals DOMPurifier, nonces of specifieke cryptografische tokens) gebruikt moesten worden, kregen we een resultaat dat standhoudt in een professionele security-audit.
De verleiding van AI is groot: met één druk op de knop staat er een functionele applicatie. Maar zoals ons onderzoek aantoont, is 'werkende code' niet synoniem aan 'veilige code'. AI is een uitstekende assistent om meters te maken, maar een matige architect voor je digitale veiligheid. Het kritische verschil zit hem in de regie; zonder een expert die de vinger op de zere plek legt, blijft de beveiliging een gok met hoge inzet.
Gebruik AI dus vooral om je efficiëntie te verhogen, maar laat de fundering van je applicatie nooit over aan een algoritme dat functionaliteit boven veiligheid verkiest. Want uiteindelijk is een snelle oplevering weinig waard als de achterdeur onbedoeld open blijft staan.