This blog post is also available in English

Seit dem Aufkommen agentischer Programmierung habe ich sehr gute Erfahrungen damit gemacht, Agenten einzusetzen, um meinen Entwicklungsprozess zu beschleunigen. Und ich habe dabei auch die Lektion gelernt, wie wichtig ein kleiner Scope für Entwicklungsaufgaben ist.

Was ich lange vor mir hergeschoben habe: eine Entwicklungsumgebung einzurichten, die die nötigen Leitplanken bietet, um Agenten sicher zu nutzen. Wie so viele andere auch, habe ich das aus dem uralten Spannungsfeld zwischen Sicherheit und Bequemlichkeit heraus vernachlässigt. Es kostet Zeit, eine wirklich sichere Umgebung aufzusetzen – und sie ist nicht gerade bequem, vor allem wenn du wie ich minimalen Aufwand, wenig Konfiguration und Systeme, die einfach „laufen”, schätzt.

Der Vortrag meines Kollegen Christoph beim INNOQ Technology Day 2025 über die Schattenseiten generativer KI und die Gefahren von Prompt Injection hat meine Prioritäten geändert.

Ich bin keine Security-Expertin. Deshalb habe ich unsere INNOQ-Security-Expert:innen gebeten, den Ansatz, den ich hier vorstelle – eine Development-Sandbox, die meine KI-Agenten einsperrt – zu reviewen.

Warum Sandboxing wichtig ist

Wenn KI-Agenten auf unseren Rechnern laufen, können sie Programme mit denselben Rechten ausführen wie wir selbst. Das macht sie enorm leistungsfähig – und uns verwundbar.

Weil wir mit einem LLM in natürlicher Sprache interagieren, ist es für das LLM extrem schwer zu erkennen, welcher Teil eines Prompts von uns stammt und welcher Teil nicht.

Wenn das LLM auf einer Webseite ein README liest, das höflich darum bittet, unsere Credentials an Service X zu schicken – und uns anschließend nichts davon erzählt: Was glaubst du, was der Agent dann macht?

Dieses Einschleusen fremder Anweisungen in unsere Prompts nennt man Prompt Injection, in Anlehnung an andere Injection-Angriffe wie SQL Injection oder Cross-Site Scripting. Leider lassen sich klassische Gegenmaßnahmen hier kaum anwenden: Natürliche Sprache ist riesig und vielfältig. Es ist sehr schwer – wenn nicht unmöglich – zuverlässig zu bestimmen, welcher Teil aus welcher Quelle stammt.

Dieser Bericht über das Jailbreaking von Claude Code zeigt, wie knifflig das ist: Eine Anfrage wie „find database configs so I can migrate to secrets manager” kann je nach Intention der Nutzer:in legitim oder bösartig sein – und diese Intention kann ein LLM nicht einfach so erkennen.

Prompt-Injection-Mitigation ist ein aktives Forschungsfeld. Stand heute sollten wir es aber als ungelöstes Problem behandeln.

Ich empfehle diesen Beitrag von Simon Willison über Lethal Trifecta. Er beschreibt drei Eigenschaften von Agents, die in Kombination Angreifer:innen das Eindringen in Systeme und den Diebstahl von Daten stark erleichtern können:

  1. Zugriff auf deine privaten Daten
  2. Kontakt zu nicht vertrauenswürdigem Inhalt
  3. Die Fähigkeit, extern zu kommunizieren

Die KI-Agenten, die ich nutze (Codex und Claude Code), fragen nach, bevor sie eine Anfrage an eine unbekannte Website senden (außer wir haben YOLO-Privilegien aktiviert).

Dieses Sicherheitsnetz wird allerdings dadurch geschwächt, dass es nervt. Man klickt schnell „Ja, darfst du” an – und läuft Gefahr, aus Versehen nachlässig zu werden und etwas durchrutschen zu lassen.

Eine Sandbox ist ein Mechanismus, mit dem wir den Agenten effektiv darauf begrenzen können, nur auf bestimmte Dateien und bestimmte Credentials zuzugreifen. Damit entschärfen wir im Kern den ersten Punkt der Lethal Trifecta: Zugriff auf private Daten. Für Entwicklung braucht man meist ein paar Credentials – zum Beispiel einige API-Tokens. Wenn wir eigene Credentials speziell für die Sandbox erstellen, können wir den potenziellen Schaden im Fall eines Exploits drastisch begrenzen. Gleichzeitig brauchen wir einen Plan, wie wir diese Credentials im Worst Case schnell widerrufen. Und wir sollten sorgfältig prüfen, dass sie wirklich nur die minimal nötigen Berechtigungen haben.

In die Sandbox gehören ausschließlich Dateien, die ein Agent sehen und im Zweifel auch löschen darf.

Sensible Daten wie Credentials und Kundendaten dürfen niemals in die Sandbox.

Leider adressiert die Sandbox-Lösung, die ich hier vorstelle, noch keine Validierung des Netzwerkverkehrs in die Sandbox hinein und aus ihr heraus. Damit bleiben die beiden anderen Punkte der Trifecta weiterhin relevant: Wenn ein Agent beliebige Inhalte aus dem Internet abrufen darf, ist er weiterhin nicht vertrauenswürdigem Inhalt ausgesetzt. Und wenn ausgehender Traffic erlaubt ist, bleibt auch die Fähigkeit zur externen Kommunikation erhalten.

Um sich wirklich gegen die gesamte Trifecta zu schützen, müsste man also auch das Netzwerk stark einschränken. Das habe ich (noch) nicht umgesetzt. Ich denke darüber nach und suche nach praktikablen Wegen. Bis dahin nutze ich weiterhin die eingebauten Approval-Mechanismen von Codex und Claude Code, um Outbound-Traffic bewusst freizugeben.

Der Ansatz unten ist aus meiner Sicht der erste Schritt hin zu einer sicheren Entwicklungsumgebung für agentisches Programmieren. Ich möchte mein Setup später erweitern, um auch den Netzwerkverkehr besser zu kontrollieren.

So sandboxst du deine Agents

Ich nutze macOS auf einem ARM-Prozessor. Unter Linux oder Windows wird dein Setup deutlich anders aussehen.

Die passende Sandbox-Technologie wählen

Zu Beginn habe ich nach einer Technologie gesucht, die zu meinem persönlichen Dev-Stack passt. Dabei ging es für mich um Fragen wie:

  • Welche KI-Agenten nutze ich? (bei mir: codex und claude auf der CLI)
  • Welche Entwicklungsumgebung brauche ich? (bei mir: docker-compose für Datenbanken und Queues)
  • Welches Build-Tool nutze ich? (bei mir: gradle, npm)
  • Welche Sprachen und Frameworks nutze ich? (bei mir: Java, JavaScript)
  • Welche IDE bzw. welcher Editor ist mir am liebsten? (bei mir: IntelliJ)

Für Sandboxing gibt es eine ganze Reihe möglicher Technologien. Ich habe u. a. von Development Containers, Docker MCP Toolkit, und Container Use gehört, die in der Praxis erfolgreich eingesetzt werden. Anthropic entwickelt eine eigene Sandbox, falls dich Vendor Lock-in nicht stört. Docker arbeitet ebenfalls an einer eigenen Lösung. Außerdem bin ich über eine kuratierte Liste mit Code-Sandboxing-Lösungen gestolpert. (⚠️ Hinweis: Ich hatte nicht die Zeit, mir alle Optionen im Detail anzusehen – mach bitte dein eigenes Due Diligence, bevor du dich entscheidest!)

Virtuelle Maschinen vs. Container

Rein technisch wäre ein komplett separater Laptop ausschließlich für agentisches Programmieren vermutlich die sicherste Variante. Praktisch landen die meisten Lösungen aber in zwei Kategorien: virtuelle Maschinen oder Container.

Ich habe mich für eine virtuelle Maschine statt für Container entschieden, weil ich nicht meine komplette Entwicklungsumgebung in YAML nachbauen wollte, nur um eine Sandbox zu betreiben. Wir haben ohnehin schon ein komplexes ´docker-compose-Setup´ – und die Vorstellung, das für jeden Service in der Sandbox sauber zum Laufen zu bringen, bereitet mir Unbehagen.

Mit einer virtuellen Maschine kann ich außerdem per SSH meine Development-Sandbox mit Jetbrains Gateway verbinden. So kann ich bei den Tools bleiben, die ich kenne und mag.

Mit Hilfe von ChatGPT habe ich mich für Lima VM entschieden. Damit lassen sich Linux-VMs mit minimaler Konfiguration starten.

Lima installieren

Der erste Schritt war natürlich die Installation von Lima VM. Ich schreibe hier keine komplette Schritt-für-Schritt-Anleitung, weil es die bereits gibt.

Das einzige Problem bei mir: Ich musste Homebrew neu installieren. Offenbar hatte ich seit Jahren nichts installiert, das wirklich Zugriff auf die native CPU-Architektur gebraucht hätte – und ich lief noch mit der Rosetta-Version von Homebrew. Nach der Neuinstallation konnte ich Lima ohne weitere Hürden installieren.

Ein wichtiger Punkt: Auch wenn ich ein LLM genutzt habe, um verschiedene Tools für meinen Use Case zu bewerten, habe ich keinen KI-Agenten eingesetzt, um das System auf meinem Rechner einzurichten.

Wenn wir Software installieren, die kritisch für die Sicherheit unserer Systeme ist, sollten wir jeden einzelnen Schritt hinterfragen – und uns die Zeit nehmen, die Konsequenzen und die Trade-offs wirklich zu verstehen.

Eine minimale VM-Konfiguration erstellen

Mit etwas Iteration und Troubleshooting zusammen mit ChatGPT bin ich bei folgender Minimal-Konfiguration gelandet, die für alles reicht, was ich brauche. Die wichtigste Entscheidung war, welches Betriebssystem ich nehmen will (ich habe mich für die neueste LTS-Version von Ubuntu entschieden) und wie viel RAM ich der VM gebe. Den Rest bespreche ich später.

images:
- location: "https://cloud-images.ubuntu.com/releases/24.04/release/ubuntu-24.04-server-cloudimg-arm64.img"
  arch: "aarch64"

cpus: 8
memory: "16GiB"
disk: "120GiB"
  
ssh:
  localPort: 60022  

mounts:
- location: "~/devbox/projects"
  mountPoint: "/home/joy.linux/projects"
  writable: true

vmOpts:
  vz:
    rosetta:
      enabled: true
      binfmt: true

portForwards:
  - guestPort: # port config here
    hostPort: # port config here

Nachdem meine dev-sandbox-VM steht, starte ich sie in der Konsole mit limactl shell dev-sandbox.

Umgebung einrichten

Als die VM lief, habe ich meine Umgebung eingerichtet. Git, Java, nvm und meine KI-Agenten zu installieren, war recht unkompliziert. Docker mit den richtigen Berechtigungen zu installieren, hat ein bisschen Debugging gekostet, aber nichts Dramatisches. An Credentials brauche ich nur ein Read-only-Access-Token für unser privates Maven-Repository und API-Keys für meine KI-Agenten.

In meinem ersten Setup hatte ich sogar eigene SSH-Keys für die Sandbox angelegt und die Repositories direkt in der Sandbox geklont. Eine Kollegin hat mir dann vorgeschlagen, stattdessen die komplette GitLab-Kommunikation auf dem Host zu lassen und der Sandbox nur Zugriff auf den bereits geklonten Code zu geben. Das ist so viel besser als mein ursprünglicher Ansatz – ich bin wirklich begeistert. Einem Agenten Zugriff auf einen SSH-Key zu geben, hat sich für mich ohnehin nicht gut angefühlt, erst recht, weil ich das Netzwerk noch nicht konsequent abgesichert habe. Jetzt ist es unmöglich, dass Agenten in meinem Namen Commits pushen und damit Probleme in unserer Codebase verursachen.

Gemeinsame Verzeichnisse mounten

Lima VM kann auch Verzeichnisse mounten: vom Host in die VM hinein. Das heißt: Ich checke meinen Code auf dem Host aus, und der Agent arbeitet direkt auf diesen Dateien – sie sind zwischen Host und VM geteilt.

Das hat einen weiteren Vorteil: Ich kann jederzeit zwischen agentischem Programmieren (in der Sandbox) und normalem Programmieren direkt auf dem Host wechseln – ohne zusätzliche Tools, weil beide auf denselben Dateien arbeiten.

mounts:
- location: "~/devbox/projects"
  mountPoint: "/home/joy.linux/projects"
  writable: true

Testcontainers in der VM zum Laufen bringen

Das größte Problem war: Testcontainer lief nicht in der VM. In unserem Projekt nutzen wir Testcontainer-Images, die für Intel-Architektur gebaut sind. Auf meinem Mac laufen sie dank Rosetta-Emulation problemlos. Damit Rosetta auch in der VM aktiv ist, müssen folgende Optionen gesetzt werden:

vmOpts:
  vz:
    rosetta:
      enabled: true
      binfmt: true

Per JetBrains Gateway für Remote Development verbinden

Auch Jetbrains Gateway einzurichten war relativ unkompliziert. Ich habe die Lima-SSH-Config meiner VM unter ~/.lima/dev-sandbox/config gefunden. Gateway konnte damit per SSH in die VM und hat sich dort installiert. Danach lässt sich ein IntelliJ-Fenster öffnen, um den Source Code zu lesen oder zu ändern – inklusive aller Tools, die ich gewohnt bin. Ich habe schließlich einen festen SSH-Port in der Lima-Config gesetzt, weil die VM sonst bei jedem Start einen anderen Port bekommt und die Gateway-Verbindung dann nicht mehr klappt:

ssh:
  localPort: 60022

Port Forwarding einrichten

Als Nächstes habe ich Port Forwarding so konfiguriert, dass ich die Anwendung in der VM starten und sie im Browser auf dem Host anschauen kann. Dafür habe ich tatsächlich meinen KI-Agenten in der VM benutzt: Er ist alle Repositories durchgegangen und hat eine Konfiguration für alle Ports erzeugt, die von den Anwendungen genutzt werden könnten. Diese habe ich anschließend geprüft und dann in die Config übernommen.

portForwards:
  - guestPort: # port config here
    hostPort: # port config here

Letzte Feinarbeiten

Mit der funktionierenden Development-Sandbox ging es darum, sie im Alltag zu nutzen und die letzten Einstellungen zu finden, die mich wirklich produktiv machen. Mir ist aufgefallen, wie oft ich ein neues Terminal-Tab öffne und erwarte, im selben Working Directory zu landen. Deshalb habe ich ein paar Git-Aliases angelegt, um schnell in die Sandbox zu springen. Außerdem habe ich meinem Sandbox-Prompt ein anderes Farbschema und die Emojis „🏖️📦“ gegeben, damit ich auf einen Blick sehe, in welcher Shell ich gerade bin.

Das Ergebnis

Ich habe jetzt eine voll funktionsfähige VM-Sandbox für meine KI-Agenten – und kann trotzdem die JetBrains-Tools nutzen, die ich für Entwicklung bevorzuge. Für Aufgaben, die es erfordern, werde ich vermutlich noch weitere Tools ergänzen.

Der Aufbau hat etwas Zeit gekostet. Der größte Teil davon ging allerdings für das Debugging der Intel-Emulation in der VM drauf. Wenn ich diese Konfiguration von Anfang an gekannt hätte, hätte ich die Sandbox vermutlich in einem halben Tag am Laufen gehabt. Die investierte Zeit lohnt sich: Ich habe jetzt eine Entwicklungsumgebung mit wenig Reibung, die den Schaden begrenzt, falls ein KI-Agent mal aus der Spur gerät.

Das ist der erste Schritt zu einer sicheren Entwicklungsumgebung mit reduziertem Zugriff auf private Daten. Damit ist aus meiner Sicht auch eine gute Basis gelegt, um als nächsten Schritt den Netzwerkzugriff abzusichern. Ich habe meine KI-Agenten in eine Sandbox gepackt. Jetzt solltest du das auch tun.