SSD Nodes Learn 🎉 VPS vanaf $5.50/mnd
Gidsen Matt ConnorDoor Matt Connor

Prompt injection bij coding agents: risico's en defensie

Coding agents verwerken externe tekst als instructies. Ontdek de kwetsbare aanvalsoppervlakken op uw server en leer welke verdedigingsstrategieën de schade daadwerkelijk beperken.

Wat prompt injection bij een coding agent inhoudt

Prompt injection bij een coding agent is eenvoudig uit te leggen: tekst die de agent leest, wordt behandeld als een instructie die de agent opvolgt. De agent opent een bestand, een pull request-commentaar, een webpagina of het resultaat van een tool-aanroep. Dit alles komt binnen als hetzelfde type tekst als uw eigen verzoek. Als een aanvaller controle heeft over een deel van die tekst, schrijft de aanvaller in uw sessie.

Elk huidig agent-product heeft deze eigenschap. Het model ontvangt één reeks tokens. Uw verzoek, de system prompt, de bestandsinhoud en de resultaten van tools worden samengevoegd, waarna het model voorspelt wat er volgt. Er bestaat geen privilege-bit op een token. Niets in het formaat geeft aan welk deel door u is geautoriseerd en welk deel afkomstig is uit het README-bestand van een vreemde.

Deze pagina vormt het dreigingsmodel: waar door een aanvaller gecontroleerde tekst een agent op een server bereikt, wat een aanvaller op elk punt wint en welke verdedigingsmaatregelen de moeite waard zijn. Het advies over isolatie in onze andere handleidingen is pas zinvol zodra u weet waartegen u de agent moet isoleren.

Waarom het model inhoud niet van instructies kan scheiden

Training helpt, maar lost het probleem niet op. Huidige modellen zijn getraind om opgehaalde tekst met argwaan te behandelen en ze weigeren veel grove pogingen. Een weigering is een waarschijnlijkheid, geen regel. Een aanvaller kan de tekst herformuleren, opnieuw proberen en verbergen in een formaat waar niemand rekening mee heeft gehouden, en er is geen limiet aan het aantal formuleringen dat zij kunnen proberen.

Het OWASP GenAI-project volgt dit als LLM01:2025 Prompt Injection en splitst het in tweeën. Directe injectie is de eigen prompt van een gebruiker die het gedrag van het model verandert. Indirecte injectie is externe inhoud, zoals een website of een bestand, die het gedrag verandert wanneer het model dit verwerkt. Indirecte injectie is de vorm die ertoe doet op een server, omdat een agent veel meer tekst leest dan u typt.

De eerste systematische studie is Greshake en collega's, Not what you've signed up for (2023). Hun conclusie is de zin die u moet onthouden: wanneer een applicatie opgehaalde tekst invoert in een model dat tools kan aanroepen, staat het verwerken van die tekst gelijk aan willekeurige code-executie.

De voorwaarde die een leesactie verandert in een inbreuk

Het lezen van kwaadaardige tekst is op zichzelf geen schade. De schade vereist een uitweg uit de machine.

Simon Willison noemde die combinatie de dodelijke trifecta in juni 2025. Een agent die privégegevens bevat, wordt blootgesteld aan niet-vertrouwde inhoud en gegevens naar buiten kan sturen, kan worden overgehaald om de eerste via de derde naar buiten te sluizen.

Een programmeer-agent op uw VPS beschikt vanaf de eerste dag over al deze elementen. De privégegevens zijn uw broncode, uw .env-bestand, uw SSH-sleutels en uw shell-geschiedenis. De niet-vertrouwde inhoud is elk repository, elke pagina en elk resultaat van een tool dat de agent leest. De uitweg is git push, curl, npm publish, de inhoud van een pull request of een link die in uw terminal wordt getoond en waarop u klikt.

U kunt de tweede voorwaarde niet wegnemen, omdat het lezen van niet-vertrouwde tekst de taak is waarvoor u de agent heeft ingehuurd. Elke praktische verdediging richt zich daarom op de andere twee.

Waar niet-vertrouwde tekst een coding agent op een server bereikt

De repository waarin de agent werkt

Elk bestand in de checkout is invoer. Broncodecommentaar, README.md, changelogs, test-fixtures, vendored code en de instructiebestanden van de agent zelf: CLAUDE.md, AGENTS.md en hun equivalenten. Een agent die de opdracht krijgt een codebase te begrijpen, leest deze bestanden, omdat u daarom heeft gevraagd.

Wat een aanvaller hier wint, is toegang tot iedereen die de repository kloont en er een agent op loslaat. Instructiebestanden zijn de meest directe route, omdat ze bedoeld zijn om als instructies te worden gelezen. Een pull request dat vier nuttige regels toevoegt aan CLAUDE.md en één regel die de agent omleidt, is een wijziging waar een menselijke reviewer snel overheen kijkt.

Issues, pull requests en code review-commentaren

Alles wat een vreemde in uw tracker kan typen, bereikt de agent op het moment dat u vraagt om triage. In mei 2025 publiceerde Invariant Labs een GitHub MCP-bevinding met precies deze vorm. De agent van een ontwikkelaar had toegang tot één publieke repository en tot private repositories. Een aanvaller opende een issue in de publieke repository. Toen de ontwikkelaar de agent vroeg om naar openstaande issues te kijken, las de agent de inhoud van de private repository en schreef deze in een pull request aan de publieke kant.

Dat rapport beschrijft een architectonisch probleem in plaats van een defect in de MCP-server. De agent beschikte over één breed toegangs-token, las uit een publieke inbox en had schrijfrechten. Er was in de gebruikelijke zin niets verkeerd geconfigureerd; daarom is het antwoord scoping en niet patchen.

Webpagina's die de agent ophaalt

Documentatie, forumantwoorden, een leverancierspagina, een zoekresultaat. Alles kan tekst bevatten die voor de agent is geschreven in plaats van voor u. HTML die naar tekst wordt geconverteerd, geeft een aanvaller extra ruimte, omdat inhoud die een browser nooit weergeeft, toch het model bereikt.

Een aanvaller krijgt controle op het moment dat de agent het minst in de gaten wordt gehouden. Niemand leest de volledige tekst van een pagina die de agent heeft opgehaald tijdens het opzoeken van informatie.

MCP-tooloutput

MCP (model context protocol) is de gebruikelijke manier waarop agents verbinding maken met externe tools. Resultaten komen terug als tekst en gaan direct in het contextvenster. Er zijn hier twee aanvalsoppervlakken, niet één. De data die een tool teruggeeft is de voor de hand liggende. De naam en beschrijving van de tool zelf, die het model leest om te beslissen wanneer het de tool moet aanroepen, is de andere; een server die u niet beheert, kan beide wijzigen tussen aanroepen door.

Een aanvaller die tekst in de output van één tool plaatst, bereikt elke andere tool die de agent heeft. Dat is hoe een injectie in iets van lage waarde uiteindelijk iets van hoge waarde aanstuurt.

CI-logs, build-output en dependency-metadata

npm install print tekst van pakketten die u niet zelf heeft geschreven. Een testfout print een assertiebericht van een bibliotheek. Een logbestand van een continuous integration (CI)-job bevat duizenden regels output van derden. Vraag een agent om een falende build te repareren en deze leest het allemaal.

Hier wint een aanvaller de build-machine, die meestal over deploy-credentials en registry-tokens beschikt en minder aandacht krijgt dan een laptop.

Wat een aanvaller daadwerkelijk verkrijgt

Er zijn vier uitkomsten waar u rekening mee moet houden.

Diefstal van inloggegevens. Alles wat het agent-proces kan lezen, valt binnen het bereik: omgevingsvariabelen, ~/.aws/credentials, ~/.ssh, een gh-token, of een Docker-configuratiebestand. Voor het versturen hiervan is geen curl vereist. Een commit naar een branch, een beschrijving van een pull request, een pakket dat naar een registry wordt gepubliceerd, of een DNS-opzoeking voor een naam die de aanvaller beheert, zijn allemaal manieren om gegevens van de server te krijgen.

Door u goedgekeurde codewijzigingen. Het schrijven van code is de kerntaak van de agent; het laten schrijven van een subtiel foutieve regel is daarom de eenvoudigste uitkomst voor een aanvaller. Denk aan een toegevoegde dependency of een logging-aanroep die een token meestuurt naar een logbestand dat u elders opslaat.

Persistentie. Een eenmaal geschreven bestand blijft werken zonder dat er een model aan te pas komt: een hook in .git/hooks, een postinstall-script in package.json, een regel toegevoegd aan een shell-opstartbestand, of een extra regel in CLAUDE.md. Het volgende commando voert het uit.

Verplaatsing binnen uw netwerk. De agent draait op de locatie waar u deze plaatst. Als die server een database op loopback bereikt, een interne beheerservice, de metadata-service van uw cloudprovider, of een andere host op het privénetwerk, dan geldt dat ook voor alles wat de agent aanstuurt.

Auto-approve-modi verwijderen de laatste controle

In de standaardmodus vraagt Claude Code om bevestiging voordat een commando wordt uitgevoerd of een bestand wordt bewerkt. Deze prompt is de menselijke controle die fungeert als buffer tussen elke bovenstaande actie en de daadwerkelijke uitvoering. Modi die de prompt verwijderen, verwijderen daarmee ook deze controle.

De documentatie is duidelijk over bypassPermissions: gebruik dit alleen in geïsoleerde omgevingen zoals containers of VM's waar Claude Code geen schade kan aanrichten. De auto-modus is minder strikt en keurt tool-aanroepen automatisch goed met veiligheidscontroles op de achtergrond die verifiëren of acties overeenkomen met uw verzoek. Deze controles vangen veel op. Het blijft echter een beoordeling door het model van de output van het model zelf; beschouw ze daarom als een filter en niet als een harde grens.

Een beheerder kan beide verwijderen. Stel permissions.disableBypassPermissionsMode of permissions.disableAutoMode in op "disable" in een instellingenbestand en plaats dat bestand in de beheerde instellingen, zodat een uitgecheckt project dit niet kan overschrijven. Onze handleiding over Claude Code auto-modus en permissieregels beschrijft waar elke regel van kracht is.

Verdedigingslinies, gerangschikt op hun effectiviteit

Geen van deze maatregelen is een definitieve oplossing. Elke maatregel beperkt ofwel de toegang van de agent, ofwel wat de agent daarmee kan uitvoeren.

  1. Een machine die u kunt vernietigen en opnieuw opbouwen, zodat een inbreuk u een uur kost in plaats van een incident.
  2. Inloggegevens die gescheiden zijn van uw eigen gegevens, beperkt tot één repository en met een korte levensduur.
  3. Geen langdurige geheimen in de omgeving die de commando's van de agent overneemt.
  4. Handhaving door het besturingssysteem op netwerkverkeer en bestandstoegang, wat geldt voor elk proces dat de agent start.
  5. Goedkeuringsprompts ingeschakeld voor schrijfacties en netwerkverzoeken.
  6. Hooks als een deterministische vangnet voor specifieke acties die u kunt benoemen.
  7. Het lezen van de diff voordat u deze samenvoegt (merge).

De volgorde is van belang. Punten 1 tot en met 4 blijven van kracht, zelfs wanneer het model volledig onder controle van een aanvaller staat. Punten 5 tot en met 7 zijn afhankelijk van een mens die oplet, en precies dat stopt naarmate een agent langer draait.

Plaats de agent op een machine die u kunt weggooien

Een VPS die een checkout en één beperkt token bevat, is een veel minder aantrekkelijk doelwit dan een laptop met uw eigen sleutels. Voer de agent uit als een eigen gebruiker zonder privileges, niet als uw inlogaccount en niet als root. Onze handleidingen over een wegwerp-VM voor coding agents en gebruikers met minimale rechten op een VPS behandelen de configuratie, en veilig Claude Code draaien op een VPS behandelt het dagelijks gebruik.

Verwijder geheimen uit de omgeving

Een omgevingsvariabele is leesbaar voor elk onderliggend proces, wat betekent dat elk commando dat de agent uitvoert erbij kan. De sandbox van Claude Code kan benoemde variabelen verwijderen vóór elk gesandboxte commando. Op Linux heeft de sandbox eerst twee pakketten nodig:

sudo apt-get install bubblewrap socat

Vervolgens in ~/.claude/settings.json:

{
  "sandbox": {
    "enabled": true,
    "network": {
      "allowedDomains": ["github.com", "*.npmjs.org"]
    },
    "credentials": {
      "envVars": [
        { "name": "GITHUB_TOKEN", "mode": "deny" },
        { "name": "NPM_TOKEN", "mode": "deny" }
      ]
    }
  }
}

Een deny-item verwijdert die variabele voordat elk gesandboxte commando wordt uitgevoerd, en allowedDomains beperkt gesandboxte commando's tot de hosts die u opgeeft. Het credentials-blok vereist Claude Code v2.1.187 of nieuwer, gecontroleerd in augustus 2026. Voer /sandbox uit in een sessie om te zien welke lagen actief zijn en welke afhankelijkheden ontbreken. Bepalen welke geheimen überhaupt op die machine moeten staan, is het grootste deel van het werk, en geheimen buiten het bereik van een AI-agent houden werkt dit verder uit.

Blokkeer netwerkverkeer op besturingssysteemniveau

Een firewallregel trekt zich niets aan van wat het model heeft besloten. Voer de agent uit als een toegewezen agent-gebruiker en blokkeer vervolgens wat die gebruiker verstuurt:

table inet agentcage {
  chain output {
    type filter hook output priority filter; policy accept;
    meta skuid "agent" ct state established,related accept
    meta skuid "agent" oif lo accept
    meta skuid "agent" counter drop
  }
}

Dat laat de agent-gebruiker alleen met loopback-toegang, waardoor het verkeer via een proxy moet lopen die u op dezelfde machine draait, en de proxy bevat de toegestane hostnamen. Met https_proxy gericht op die proxy, verstuurt de client een CONNECT-verzoek en voert de proxy de naamopzoeking uit, waardoor de agent zelf geen uitgaande DNS (domain name system) nodig heeft. Controleer uw werk met sudo nft list ruleset en bekijk hoe de teller op de blokkeerregel oploopt terwijl de agent probeert iets nieuws te bereiken.

Houd een tweede SSH-sessie open terwijl u firewallwijzigingen doorvoert. Controleer ook wat uw container-runtime met deze regels doet: Docker schrijft zijn eigen chains, en gepubliceerde Docker-poorten omzeilen ufw beschrijft de verrassing die dat veroorzaakt.

Hooks: de controle waar het model niet omheen kan praten

Toegangsregels en hooks worden afgedwongen door Claude Code, niet door het model. De documentatie stelt het duidelijk: instructies in uw prompt of CLAUDE.md vormen wat Claude probeert te doen, en ze veranderen niets aan wat Claude Code toestaat. Dat onderscheid is de volledige waarde. Een regel in CLAUDE.md met de tekst "voer nooit curl uit" is een suggestie waar een geïnjecteerde alinea tegenin kan gaan. Een hook is een proces dat een exitcode teruggeeft.

Registreer een PreToolUse-hook in .claude/settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/no-egress.sh"
          }
        ]
      }
    ]
  }
}

De hook ontvangt de tool-aanroep als JSON via standaardinvoer. Exitcode 2 blokkeert de aanroep en toont Claude de reden via standaardfout. Exitcode 0 laat de aanroep doorgaan via de normale toestemmingsstroom.

#!/usr/bin/env bash
# PreToolUse: stdin holds the tool call, exit 2 blocks it.
cmd=$(jq -r '.tool_input.command // ""')
if printf '%s' "$cmd" | grep -qE '(^|[;&|]|\s)(curl|wget|nc|ncat)(\s|$)'; then
  echo "Blocked: this repository does not allow outbound network commands." >&2
  exit 2
fi
exit 0

Nu het eerlijke deel. Dit is een zwarte lijst op basis van een shell-string, en zwarte lijsten op shell-strings lekken. python3 -c opent een socket zonder het woord curl te gebruiken. Een make deploy-doel verbergt dezelfde aanroep een niveau dieper. Schrijf hooks voor de fouten die u kunt benoemen, en plaats de grens waar u daadwerkelijk op vertrouwt in de kernel of op het netwerk.

Toegangsregels kennen een limiet die het waard is om te weten. Read en Edit-ontzeggingsregels dekken de eigen bestandstools van Claude en de bestandscommando's die het herkent in Bash, zoals cat, head, tail en sed. Ze dekken geen Python- of Node-script dat het bestand zelf opent. Regels worden geëvalueerd met ontzegging eerst, dan vragen, dan toestaan, dus een ontzeggingsregel kan geen uitzondering voor een toegestane lijst bevatten.

{
  "permissions": {
    "deny": [
      "Read(.env)",
      "Read(./secrets/**)",
      "Bash(git push *)"
    ]
  }
}

Monitor wat vertrekt en lees de diff

Een agent-run produceert een diff en een set netwerkaanroepen. Beide verdienen een blik voordat er iets wordt samengevoegd of geïmplementeerd. Een zelfgehoste beveiligingsreview van de diff vangt een andere klasse wijzigingen op dan een menselijke scan, en weten wat een coding agent naar buiten stuurt vertelt u hoe normaal verkeer eruitziet, zodat een vreemd verzoek opvalt.

Wat is nog onopgelost

Er bestaat vandaag de dag geen betrouwbare scheiding tussen inhoud en instructie. Elke verdediging die wordt uitgebracht is ofwel een filter met een foutmarge, ofwel een beperking van de gevolgen. Niets in de stack markeert een tekstblok als data die nooit mag worden opgevolgd.

Filters helpen wel degelijk, maar ze falen ook. Een classifier die de meeste injectiepogingen onderschept, moet elke keer gelijk hebben, terwijl een aanvaller slechts één keer gelijk hoeft te hebben. Die asymmetrie is de reden waarom een gepubliceerd succespercentage van een verdediging het startpunt is voor de volgende poging, in plaats van een garantie.

Het meest veelbelovende werk bevindt zich op het niveau van het ontwerp in plaats van op het niveau van het model. CaMeL, uit Defeating Prompt Injections by Design (Debenedetti en collega's, 2025), extraheert eerst de control flow en data flow uit het vertrouwde verzoek, zodat niet-vertrouwde data niet kan veranderen wat het programma doet, en dwingt vervolgens capability-checks af wanneer tools worden aangeroepen. De eigen cijfers van het paper op de AgentDojo-benchmark laten zien wat dat kost.

ChartAgentDojo tasks solved, published figures from the CaMeL paper (2025)
The data behind this chart
[
  {
    "label": "Undefended agent",
    "tasks_solved_pct": 84
  },
  {
    "label": "CaMeL",
    "tasks_solved_pct": 77
  }
]

De onverdedigde agent loste 84 procent van de taken op. CaMeL loste 77 procent op met een bijbehorende beveiligingsgarantie. Dit zijn de gepubliceerde cijfers van het paper op één benchmark en geen meting van uw eigen werklast. Het verschil daartussen is grofweg wat een echte garantie vandaag de dag kost.

Totdat een dergelijk ontwerp wordt uitgebracht in de tools die u dagelijks gebruikt, moet u ervan uitgaan dat de agent op enig moment gecompromitteerd raakt en moet u zorgen dat die gebeurtenis onbeduidend is. Dat is het hele argument voor een wegwerpmachine, beperkte credentials, gecontroleerd uitgaand verkeer en de gewoonte om de diff te lezen.

FAQ

Kan ik prompt injection voorkomen door de agent te instrueren instructies in bestanden te negeren?

Nee. Die zin is tekst in hetzelfde contextvenster als de aanval en concurreert op gelijke voet met de tekst van de aanvaller. De documentatie van Claude Code trekt hier een duidelijke grens: instructies in uw prompt of CLAUDE.md bepalen wat de agent probeert te doen, maar ze veranderen niets aan wat de tool toestaat. Beschouw een instructiebestand als een intentieverklaring en plaats alles waar u op vertrouwt in toegangsregels, een PreToolUse hook of een firewallregel.

Is prompt injection een reëel risico als de agent alleen mijn eigen repository aanraakt?

Ja, want uw repository staat vol met tekst die u niet zelf heeft geschreven. README-bestanden van dependencies, URL's in lockfiles, test-fixtures, vendored code en de output van npm install komen allemaal binnen tijdens een normale taak. Alles wat uit een issue tracker of een documentatiesite wordt gehaald, komt op dezelfde manier binnen. Het risico groeit naarmate de agent meer leest, en een nuttige agent leest veel.

Lost het draaien van de agent in een container dit op?

Het beperkt de schade, maar alleen als u ook de inloggegevens verwijdert. Een container met uw doorgegeven SSH-agent, cloud-inloggegevens in de omgeving en onbeperkte netwerktoegang geeft een aanvaller bijna alles wat de host ook zou bieden. Wat de container u werkelijk oplevert, is een bestandssysteem dat u kunt verwijderen en een schone plek om uitgaand netwerkverkeer (egress) te beperken. Combineer dit met een token dat beperkt is tot één repository.

Welke enkele wijziging verkleint het risico het meest?

Verwijder langdurig geldige inloggegevens uit de omgeving die de commando's van de agent overneemt en geef die machine vervolgens een default-deny beleid voor uitgaand netwerkverkeer. Samen doorbreken ze de derde voorwaarde in de dodelijke combinatie: tekst kan de agent nog steeds kapen, maar de data die de agent bereikt, kan nergens nuttigs heen. Goedkeuringsprompts en diff-reviews helpen ook, maar zij zijn afhankelijk van een mens die alert blijft tijdens een lange sessie; daarom staan ze lager op de prioriteitenlijst dan die twee wijzigingen.