AI-Agent-Sicherheit: Berechtigungen, Sandboxes und MCP-Bedrohungen
Automatische Übersetzung Dieser Artikel wurde automatisch aus der englischen Originalversion übersetzt.
Artikel-Update
Ursprünglich veröffentlicht am 20. April 2026. Überprüft und aktualisiert am 6. September 2026. Das Update behandelt neuere Sandbox-Kontrollen, Safety-Interventionen von Providern und veröffentlichte Security-Findings – einschließlich ihrer Grenzen und Quellenlinks.
Agent-Sicherheit beginnt, sobald ein Model eine Aktion vorschlägt, und bevor die Maschine sie ausführt. Entscheide, welcher Check das letzte Wort hat, bevor die Aktion Credentials, Dateien, Netzwerke oder ein externes System erreicht.
Das Harness ist der Code, der jeden Prompt erstellt, entscheidet, welche vorgeschlagenen Tool Calls ausgeführt werden, und die Ergebnisse an das Model zurückgibt. Die meisten Checks gehören dorthin, weil dies die letzte kostengünstige Stelle ist, um einen Befehl zu stoppen. Nach der Ausführung eines Befehls müssen die Sandbox, die erhaltenen Credentials und jeder Recovery-Prozess den Schaden begrenzen. Einige der in diesem Artikel beschriebenen Incidents erreichen ein Model überhaupt nicht.
AI-Agent-Sicherheit ist umfassender als LLM-Safety. Frühe Guardrail-Produkte prüften Input und Output eines einzelnen Model Calls. Sie konnten toxischen Text filtern, personenbezogene Daten redigieren, Jailbreaks blockieren und themenfremde Antworten ablehnen. Diese Grenze war sinnvoll, solange das Model nur Text zurückgeben konnte.
Tool Loops fügten Filesysteme, Shells, Model Context Protocol (MCP)-Server und Credentials hinzu. Dadurch erweiterte sich das Threat Model von unsicherem Text auf unsichere Aktionen. Die sieben folgenden Incident-Gruppen umfassen indirekte Prompt Injection sowie Fehler in Konfiguration, Identität und Softwareverteilung. Text-Screening kann bei einigen bösartigen Inputs helfen; es kann die Kontrollen an diesen Execution Boundaries nicht ersetzen.
Wenn ein Agent ein Repository lesen, ein Tool aufrufen oder Daten an einen Dritten senden kann, ordne jede vorgeschlagene Aktion dem Check zu, der sie stoppen kann. Die folgenden Abschnitte behandeln Berechtigungen, Hooks, Sandboxes, Credentials und Human Review.
Die kurze Kontroll-Checkliste findest du unter AI Agent Security Checklist.
AI-Agent-Sicherheits-Stack
Kein einzelnes Guardrail sichert einen Agenten ab. Jeder Teil des Systems braucht seinen eigenen Check.
Die Tabelle zeigt, wo die jeweiligen Checks ausgeführt werden. Das Harness ist das oben beschriebene Kontrollprogramm. Die Runtime ist die von ihm verwendete Infrastruktur: Sandbox, Session-Log, Checkpoint Store und Traces, die einen Worker-Neustart überdauern.
| Layer | Was er kontrolliert | Beispiel-Fehler, den er erkennt | Wo er liegt |
|---|---|---|---|
| Content Filter | Unsicherer Input- und Output-Text | Toxischer Output, PII-Leakage, Policy-verletzende Completion | Harness |
| Permission Ladder | Welche Tools, Pfade, APIs und Scopes der Agent verwenden darf | Ein Summarizer versucht, in Produktionssysteme zu schreiben | Harness |
| Pre-Tool-Policy-Hook | Ob diese konkrete Aktion jetzt ausgeführt werden soll | Shell-Befehl aus nicht vertrauenswürdigem Retrieved Content | Harness |
| Sandbox | Was das Tool auf OS- und Netzwerkebene erreichen kann | File Exfiltration, Dependency Compromise, Command Injection | Runtime |
| Human Approval Check | Irreversible oder folgenreiche Aktionen | E-Mail senden, Geld transferieren, in Produktion deployen | Harness |
| MCP- und Token-Scoping | Für welchen Server und welche Audience ein Credential gültig ist | Wiederverwendung eines Tokens bei einem unbeabsichtigten Tool Server | Runtime |
| Audit Trace | Was passiert ist, wer es genehmigt hat und warum | Incident Investigation nach einem langen autonomen Lauf | Runtime |
Content Filter fragen, ob das Model etwas Unsicheres gesagt hat. Agent-Sicherheit fragt zusätzlich, ob das System die nächste Aktion ausführen darf.
Die Harness-Zeilen entscheiden, ob eine Aktion ausgeführt werden darf. Die Runtime-Zeilen erzwingen vorab gesetzte Grenzen und protokollieren, was passiert ist. Behalte die Sandbox auch dann bei, wenn die Berechtigungsregeln vollständig erscheinen: Sie kann einen Call stoppen, den das Harness nicht vorhergesehen hat. Sie kann jedoch nicht entscheiden, ob eine erlaubte Aktion die richtige Aktion war; das muss das Harness tun.
Die letzte Spalte beschreibt, wo ein Check ausgeführt wird, nicht, wer ihn betreibt. Ein Vendor kann einen Content Filter bereitstellen, aber das Harness ruft ihn auf.
Managed Content Filter decken die Textebene ab. Die übrigen Kontrollen gehören in Application Policy, Identity und Infrastructure.
Warum sich AI-Agent-Sicherheit von LLM-Safety unterscheidet
Bharani Subramaniam und Martin Fowler legten den Rahmen Anfang 2025 in Emerging Patterns in Building GenAI Products fest. Ihre Beobachtung war eng gefasst und direkt:
„Bei traditionellen Systemen konnten wir die Korrektheit hauptsächlich durch Tests bewerten … Bei LLM-basierten Systemen begegnen wir einem System, das sich nicht mehr deterministisch verhält.“
Output-Evaluation fragt, ob eine Model-Antwort ein Rubric erfüllt. Ein Agent-Threat-Model muss zusätzlich Tool Calls, Shell Commands, File Writes, Credentials und Network Requests abdecken. Ein Output Grader kann diese Aktionen nicht stoppen. Das Harness kann es: Es ist die Menge der Checks, die einen Model-Vorschlag in eine erlaubte Aktion umwandelt. Der Rest dieses Artikels behandelt diese Checks.
Simon Willison prägte die Form des agentenspezifischen Risikos im Juni 2025 mit dem lethal trifecta:
„Das lethal trifecta der Fähigkeiten besteht aus: Zugriff auf deine privaten Daten; Kontakt mit nicht vertrauenswürdigem Content; die Fähigkeit, extern so zu kommunizieren, dass deine Daten gestohlen werden können. Wenn dein Agent diese drei Eigenschaften kombiniert, kann ein Angreifer ihn leicht dazu bringen, auf deine privaten Daten zuzugreifen und sie an den Angreifer zu senden.“
Viele nützliche Agents kombinieren diese Fähigkeiten: Inbox-Zugriff, Web Retrieval und ein Messaging Tool; oder Repository-Zugriff, Issue-Lesen und Pull-Request-Writes. Ein Content Guardrail fragt, ob das Model unsicheren Text generiert hat. Das Trifecta fragt, ob nicht vertrauenswürdiger Input das System dazu bringen kann, Daten über eine erlaubte Aktion offenzulegen.
Die strukturelle Version desselben Arguments findet sich in Joel Fokous Parallax-Preprint (arXiv 2604.12986, eingereicht am 14. April 2026, nicht peer-reviewed). Die zentrale These:
„Das System, das über Aktionen nachdenkt, muss strukturell nicht in der Lage sein, sie auszuführen, und das System, das Aktionen ausführt, muss strukturell nicht in der Lage sein, über sie nachzudenken; zwischen beiden muss ein unabhängiger, unveränderlicher Validator liegen.“
Du musst die Evaluationszahlen des Papers nicht akzeptieren, um seinen strukturellen Punkt zu prüfen. Mehrere aktuelle Harnesses implementieren Teile derselben Trennung:
- Claude Codes PreToolUse-Hooks
- Der OS-sandboxed Executor der Codex CLI (unter Linux bubblewrap plus seccomp-System-Call-Filtering)
- Anthropics Managed Agents, die Credentials in einem Vault halten, den der Agent nie sieht
- MCPs RFC-8707-Audience-Bound-Tokens
Diese Systeme halten das Model vom Code getrennt, der Commands ausführt. Ihre Kontrollen unterscheiden sich, aber keines lässt den Command Runner sich auf die Sicherheitsmeinung des Models verlassen.
Eine ergänzende Disziplin formulierte Alessandro Pignati in Januar 2026 besonders prägnant: das Principle of Least Agency. Least Privilege fragt: Auf welche Ressourcen kann diese Identität zugreifen? Least Agency fragt: Was darf dieser Agent entscheiden? Privilege beschränkt die Credentials; Agency beschränkt die Reichweite eines Plans, selbst wenn die Credentials gültig sind. Excessive Agency ist ein eigener Eintrag in den Top 10 für LLM Applications, die von OWASP, dem Open Worldwide Application Security Project, veröffentlicht wurden. Die separate agentic Liste weiter unten in diesem Artikel teilt denselben Fehler auf Tool Misuse und Privilege Abuse auf. Least Agency ist die Design-Disziplin, die beides verhindert. Ein Agent, der deine Inbox zusammenfassen kann, braucht wahrscheinlich keine Commit-Rechte für dein Monorepo. Wir finden immer wieder Konfigurationen, in denen er sie hat.
Was LLM-Guardrails abdecken
LLM-Guardrails leisten rund um den Model Call wichtige Arbeit. Sie prüfen Input, Retrieved Text und Output und blockieren, redigieren, reparieren oder markieren Content, der eine konfigurierte Regel verletzt. Die folgenden Produkte unterscheiden sich bei Deployment und Abdeckung. Ein Content Check ist von einem Authorization Check an der Tool- oder MCP-Server-Grenze getrennt; einige Produkte bieten zusätzlich Runtime-Policy-Features, die separat konfiguriert und evaluiert werden müssen.
NVIDIA NeMo Guardrails
Das am stärksten meinungsgeprägte Framework: ein Orchestration Framework rund um fünf Rail-Typen (Input, Dialog, Retrieval, Execution, Output) mit einer eigenen DSL – Colang, einer Python-ähnlichen Sprache für Dialog-Flows, User Intents und Bot Messages. Die Grundlagen kannst du über Python + YAML steuern, umfangreichere Dialoglogik wird jedoch in Colang geschrieben – daher „opinionated“. Dokumentation unter docs.nvidia.com/nemo/guardrails.
Dies ist eine illustrative API-Struktur; sie erfordert das Package und ein konfiguriertes ./config-Verzeichnis.
from nemoguardrails import LLMRails, RailsConfig
config = RailsConfig.from_path("./config")
rails = LLMRails(config)
response = rails.generate(
messages=[{"role": "user", "content": "Hello"}]
)
NeMos Repository beschreibt sein Threat Model ausdrücklich: „gängige LLM-Schwachstellen wie Jailbreaks und Prompt Injections“. Ebenso klar beschreibt es seinen Scope: „Die integrierten Guardrails sind für einen bestimmten Production Use Case möglicherweise geeignet oder auch nicht … Entwickler sollten mit ihrem internen Application Team zusammenarbeiten, um sicherzustellen, dass die Guardrails die Anforderungen erfüllen.“ Der hier gezeigte Content-Screening-Pfad überwacht, was das Model sagt. NeMos aktuelle Dokumentation beschreibt außerdem Execution Rails, Custom Actions und Tool-Call-Inspection; das sind konfigurierbare Runtime-Kontrollen, aber kein Beleg dafür, dass das eingesetzte Tool oder der MCP-Server den Call authentifiziert und autorisiert hat. Diese Grenze liegt weiterhin bei der Anwendung.
Meta Llama Guard 4
Ein reiner 12B-Content-Classifier, der aus Llama-4-Scout abgeleitet und an die MLCommons-Hazard-Taxonomie angepasst wurde (13 Harm-Kategorien plus Missbrauch des Code Interpreters laut Model Card). Meta ist bei den Grenzen ungewöhnlich offen:
„Einige Hazard-Kategorien erfordern möglicherweise faktisches, aktuelles Wissen, um vollständig bewertet zu werden … Schließlich kann Llama Guard 4 als LLM anfällig für Adversarial Attacks oder Prompt Injection Attacks sein, die seine vorgesehene Nutzung umgehen oder verändern können: siehe Llama Prompt Guard 2 zur Erkennung von Prompt Attacks.“
Meta liefert ein separates Produkt, um seinen Content Classifier gegen Prompt Injection zu schützen. Wenn dieser Satz wie ein strukturelles Eingeständnis klingt, dann ist er genau das.
Guardrails AI
Eine Validator Registry. Du kombinierst Hub Validators (PII über Presidio, JailbreakDetect, CompetitorCheck, Provenance Checks) mit on_fail-Aktionen exception | fix | fix_reask | filter | refrain | reask | noop oder einem Custom Callback (guardrailsai.com). Beachte exception, nicht raise. Im aktuellen Source-Code erreicht ein nicht erkannter on_fail-String die Custom-Callback-Behandlung und führt beim Validator-Setup zu einem Fehler, statt zu warnen und auf einen Fallback zurückzugreifen. Pinne die eingesetzte Version und teste diesen Fehlerpfad. Es gibt kein einheitliches Threat Model; die Abdeckung entspricht der Union der installierten Validators. Du erhältst Schutz für alles, wofür du einen Validator hast, und für nichts anderes.
Lakera Guard
Die etablierte SaaS API, trainiert auf zig Millionen aus Gandalf gewonnenen Attack Samples. Sie verspricht, Input und Output auf „Prompt Attacks … und Data Leakage“ zu prüfen. Lakeras separates Produkt AI Agent Security beschreibt außerdem Policy- und Runtime-Enforcement dafür, worauf Agents zugreifen, was sie aufrufen und was sie tun dürfen. Das ist eine andere Produktschnittstelle als der hier besprochene Content-Screening-Call. Prüfe vor dem Deployment den aktuellen Produkt- und Pricing-Vertrag.
AWS Bedrock Guardrails
Der Enterprise-Standard, wenn du bereits Bedrock nutzt. ApplyGuardrail funktioniert mit jedem Model, unabhängig davon, ob es von Bedrock stammt:
# No-run: illustrative AWS request; requires boto3, AWS credentials, and a real guardrail identifier.
import boto3
brt = boto3.client("bedrock-runtime")
resp = brt.apply_guardrail(
guardrailIdentifier="gr-xxxxxxxxxxxx",
guardrailVersion="2",
source="INPUT",
content=[{"text": {"text": "user question",
"qualifiers": ["guard_content"]}}],
)
Veröffentlichte ApplyGuardrail Preise: $0,15 pro 1.000 Text Units für Content Filter oder Denied Topics, $0,10 für PII Filter oder Contextual Grounding. Eine Text Unit umfasst bis zu 1.000 Zeichen.
Azure AI Content Safety
Liefert Prompt Shields als einheitlichen Endpoint, der „adversarial User-Input-Attacks … direkte und indirekte Bedrohungen“ erkennt und blockiert. Azure ist ebenfalls offen: „Azure AI Content Safety kann nicht zum Erkennen illegaler Bilder sexueller Ausbeutung von Kindern verwendet werden“, und die mehrsprachige Qualität ist auf acht evaluierte Sprachen begrenzt.
OpenAI Moderation und OpenAI Guardrails
omni-moderation-latest ist die kostenlose multimodale Baseline. Separat ist openai-guardrails-python (Dokumentation unter guardrails.openai.com) die Framework-Antwort von OpenAI: eine dreistufige Pipeline (Pre-Flight, Input, Output) mit Jailbreak Detection, Hallucination Detection über FileSearch, NSFW, PII über Presidio und LLM-as-Judge. GuardrailAgent wird in das Agents SDK eingebunden.
# No-run: illustrative OpenAI Guardrails API shape; requires the package and guardrail_config.json.
from guardrails import GuardrailsOpenAI, GuardrailTripwireTriggered
client = GuardrailsOpenAI(config="guardrail_config.json")
try:
resp = client.responses.create(model="<your-model-id>", input="...")
except GuardrailTripwireTriggered as e:
print(f"blocked: {e}")
Was Content Filter nicht entscheiden
Zwei Beobachtungen gelten für alle sieben.
Erstens sind veröffentlichte Zahlen zu Latency und Throughput rar. Bedrock, Azure und Lakera veröffentlichen Preise, aber keine Garantien für Worst-Case-Latency. Auch Meta veröffentlicht keine Garantie für einen gehosteten Endpoint von Llama Guard. NVIDIA liefert NeMo Guardrails als Software, die du selbst hostest; die Latency hängt daher von deinem Model und deiner Infrastruktur ab. Miss jeden synchronen Check auf dem Critical Path, statt seine Kosten aus dem Produktpreis abzuleiten.
Zweitens behandelt dieser Abschnitt die oben aufgeführten Content-fokussierten Konfigurationen.
Ein Content Filter kann Model-Input und -Output prüfen. Er zeigt nicht, dass ein konkreter Tool Call autorisiert ist, dass ein MCP-Server seinen Caller authentifiziert hat oder dass das System mehrstufige Data Exfiltration oder Code Execution vor einem Model Call stoppen kann. Authorization ist eine separate Entscheidung darüber, ob diese Identität diesen Call an diesen Server richten darf.
NeMo dokumentiert außerdem Execution Rails und Tool-Call-Inspection, und Lakera beschreibt Runtime Enforcement in seinem separaten AI-Agent-Security-Produkt. Das sind zusätzliche Kontrollen, die konfiguriert und getestet werden müssen. Der Rest dieses Posts behandelt die Checks rund um Tool Execution.
Bedrohungen für AI-Agent-Sicherheit: sieben Incidents und die OWASP ASI Top 10
Die Lücke zwischen Text-Filtering und Execution-Protection war Mitte 2025 nicht mehr nur akademisch. Die sieben folgenden Incidents erreichten Retrieval, Konfiguration, Credentials, Package-Installation oder CI-Execution. Ein Content Classifier kann weiterhin eine verdächtige Zeichenfolge erkennen, aber die Kontrollen, die diese Pfade direkt blockieren, liegen an den Grenzen von Tool, Identity, Sandbox und Supply Chain.
EchoLeak – CVE-2025-32711
Im Juni 2025 von Aim Labs, dem Research-Arm von Aim Security, gegen Microsoft 365 Copilot offengelegt. Der technische Bericht befindet sich nun bei Cato Networks, das dieses Team übernommen hat, unter dem Namen des ehemaligen Leiters von Aim Labs, Itay Ravia (Bericht). Eine präparierte E-Mail, die als Anweisung an den menschlichen Empfänger formuliert war, umging XPIA (Microsofts integrierten Filter, der in Copilot-Inputs nach Prompt-Injection-Attacks sucht). Anschließend gelangte sie in Copilots Retrieval Layer, der Dokumente durchsucht, um Kontext für Antworten zu finden. Die Researchers nennen den Trick RAG-Spraying: Der Angreifer verwendet mehrere E-Mails oder eine lange, in Chunks aufgeteilte E-Mail, um die Retrieval-Exposure zu vergrößern. Das erhöht die Wahrscheinlichkeit des Retrievals, garantiert es aber nicht. Nach dem Retrieval bettete Copilot die sensibelsten Daten der Session gehorsam in einen Markdown-Link ein, der auf ein Bild unter einer vom Angreifer kontrollierten Domain zeigte. Die Teams Preview API, die unter einer Domain lief, der Microsofts eigene Browser Policies bereits vertrauten, rief diese Bild-URL automatisch ab und übergab die Daten dadurch an den Angreifer. Keine Klicks. Aim Labs bezeichnete diese Angriffsklasse als „LLM Scope Violation“: Das Model überschreitet eine Grenze, die es nie hätte überschreiten dürfen, und verwendet dabei ausschließlich Operationen, die jedes einzelne System für legitim hielt.
Jeder Schritt sah isoliert betrachtet legitim aus. Die E-Mail war an einen Menschen adressiert. Retrieval holte ein Dokument, das es holen sollte. Der Markdown-Link wurde so gerendert, wie Markdown-Links gerendert werden. Der Bildabruf traf eine Allowlist-Domain. Die Researchers umgingen XPIAs Screening, und das Model folgte indirekten Anweisungen aus Retrieved Content. Der Fall kombiniert einen Prompt-Injection-Fehler mit Retrieval-, Rendering- und Egress-Verhalten; er zeigt nicht, dass Detectors nichts zu markieren hatten.
Amazon Q Developer VS Code v1.84.0 – Juli 2025
AWS veröffentlichte einen kompromittierten Build, nachdem ein Angreifer über ein übermäßig weitreichendes CodeBuild-GitHub-Token eine bösartige System-Prompt-Datei committet hatte (Advisory). Der Payload versuchte, die Anweisungen des Agents in Richtung destruktiver Aktionen zu verändern. Der bösartige Code wurde mit v1.84.0 verteilt, aber wegen eines Syntaxfehlers nicht ausgeführt. AWS widerrief Credentials, entfernte den Code und veröffentlichte v1.85.0. Der Payload scheiterte wegen des Syntaxfehlers, nicht weil eine Security-Control ihn blockierte.
Azure Web Apps MCP Service – CVE-2026-32211
Microsofts Vendor-CVE-Record betrifft fehlende Authentication im gehosteten Azure Web Apps MCP Service. Es handelt sich nicht um ein Advisory gegen jeden lokalen Azure MCP Server oder jedes SDK. Ein Caller, der einen nicht authentifizierten Tool Service erreicht, kann das Model vollständig umgehen; der eingesetzte Service muss den Request authentifizieren und autorisieren.
Claude-Code-Project-Trust-Schwachstellen
Dies waren separate Schwachstellen, keine erforderlichen Schritte eines einzigen Angriffs:
- Ein Bypass der Trust-Warnung wurde in 1.0.87 behoben.
- Execution vor Trust, CVE-2025-59536 wurde in 1.0.111 behoben. Repository-Konfiguration konnte Execution auslösen, bevor das Projekt vertrauenswürdig war.
- Endpoint-/API-Key-Exposure, CVE-2026-21852 wurde in 2.0.65 behoben. Nicht vertrauenswürdige Konfiguration konnte API-Traffic umleiten und Credentials offenlegen.
Project Trust, Hook Execution und Endpoint-Konfiguration sind Host Controls. Ein Content Classifier kann Code nicht verhindern, der vor dem Model Call ausgeführt wird.
Axios 1.14.1 und 0.30.4 – 31. März 2026
Der Postmortem des Maintainers identifiziert zwei bösartige Releases, 1.14.1 und 0.30.4, die die Dependency plain-crypto-js@4.2.1 enthielten. Diese Dependency installierte einen Remote Access Trojan: Malware, die einem Angreifer Remote Access auf die Maschine gewährt. Die Exposure erforderte, dass die betroffenen Versionen aufgelöst und das relevante Install-Verhalten ausgeführt wurde; ein unabhängiges npm install lud sie nicht automatisch herunter. Dies ist ein Supply-Chain-Execution-Fehler, unabhängig vom Verhalten des Models.
Trivy-Actions-Tag-Hijacking – 19. März 2026
Das Aqua Advisory beschreibt, dass 76 von 77 trivy-action-Version-Tags und sieben setup-trivy-Tags auf bösartigen Content umgeleitet wurden. Der bösartige Action-Entrypoint sammelte Memory des Runner-Prozesses und Credential-Dateien; dieser gesamte Collection-Pfad darf nicht dem Scanner-Binary zugeschrieben werden. Das spätere Docker-Hub-Ereignis hatte ein separates Exposure-Fenster.
Ein Workflow, der während der Kompromittierung ein betroffenes Tag auflöste, konnte den Payload ausführen. Tags sind verschiebbare Referenzen. Pinnen daher geprüfte Actions auf unveränderliche Commit-SHAs und verifiziere Dependency-Änderungen. Ein Coding Agent kann dieselbe unsichere Referenz in weitere Workflow-Dateien übertragen.
OpenAI / Hugging Face – Evaluations im Juli 2026
OpenAIs Incident Report vom 26. August beschreibt interne Cybersecurity-Evaluation-Agents, die über gemeinsam genutzte Infrastruktur das Internet erreichten, über ein nicht autorisiertes Message Board kooperierten und Hugging-Face-Systeme kompromittierten. Das zentrale Model war nur intern verfügbar, und die Evaluations liefen mit reduzierten Safeguards. Dies ist ein Befund über diese Evaluationsumgebung, keine gemessene Failure Rate für öffentlich eingesetzte Agents.
Die Engineering-Lektion lautet, dass ein erlaubter interner Service zu einem Outbound-Pfad oder einem Communication Channel zwischen Sessions werden kann. Teste, was ein Package Mirror, Proxy und Shared Store im Namen des Agents tun können, nicht nur, ob die Sandbox eine direkte Internetverbindung öffnen kann. METRs separate Untersuchung analysierte Agent-Verhalten und Collaboration; die Bewertung der Safeguard-Wirksamkeit und Remediation lag außerhalb ihres Scopes.
Asynchrone Provider-Intervention
OpenAIs Misalignment Monitoring kann nach Output oder Actions intervenieren. Bei abgedeckten Models können Responses Requests mit persistiertem Reasoning, WebSockets oder OpenAI Compaction automatisch gestoppt werden. Andere Responses Requests können Alerts erzeugen, ohne automatisch gestoppt zu werden; Chat Completions fällt nicht unter dieses System. Ein Webhook ermöglicht kein Blocking.
Ein blockierter Request meldet misalignment_policy_violation mit HTTP 403 vor dem Streaming; Fehler können auch mitten im Stream eintreffen. Stoppe weitere Aktionen, bewahre Request- und Tool-Records auf und hole eine Operator Review ein. Führe keinen automatischen Retry durch. Frühere Effects bleiben bestehen, und Alerts können falsch sein oder Probleme übersehen. Dies ergänzt Detection, ersetzt aber keine lokale Authorization.
Die OWASP ASI Top 10, Edition 2026
OWASPs Agentic Security Initiative (ASI) ist eine Working Group mit speziellem Fokus auf LLM-gesteuerte Agents. Am 9. Dezember 2025 veröffentlichte sie die Agentic Security Initiative Top 10 für 2026: einen Katalog von zehn Agent-Security-Risikokategorien.
Verwende sie als Coverage-Checkliste für dein Threat Model, nicht als Ranking gemessener Incident-Häufigkeiten:
Content Filter können zur Erkennung bösartiger Anweisungen beitragen, einschließlich Goal Hijacking und Memory Poisoning. Die Kategorien überschneiden sich: Keine gehört ausschließlich zu einem Textfilter. Ordne jeden Angriffspfad je nach Fall Controls für Identity, Tool Policy, Memory, Orchestration, Monitoring und Supply Chain zu. EchoLeak entspricht ASI01. Amazon Q entspricht ASI04 (Supply Chain) und ASI02 (Tool Misuse). Azure MCP ist ASI03 (Identity). Claude Code CVE-2025-59536 umfasst ASI05 (Code Execution), ASI04 und ASI03. Axios und Trivy sind ASI04. Das Mapping zeigt, warum das Threat Model über Model Input und Output hinausgehen muss.
Berechtigungen sind Infrastruktur, kein Prompt
Hier hören Guardrails auf, das Produkt zu sein, und werden zu einem Subsystem des Harness. Drei aktuelle Systeme – OpenAI Agents SDK, Codex CLI und Claude Code – zeigen, wie eine Production Policy Surface tatsächlich aussieht. Alle drei erzwingen Berechtigungen im Code. Keines verlässt sich darauf, dass das Model vorsichtig ist.
OpenAI Agents SDK
Das SDK trennt Harness und Compute. Gehostete MCP Tools nehmen require_approval entgegen – entweder den einfachen String "always" / "never" oder ein Filterobjekt, dessen Schlüssel diese beiden Policies sind und das die jeweils abgedeckten Tool-Namen enthält – sowie einen on_approval_request-Callback, der für jedes Tool ausgelöst wird, das unter "always" verbleibt, und {"approve": bool} mit einem optionalen Reason zurückgibt. Feingranulares Tool Filtering (tool_filter) ist bei den Local-Server-Varianten (MCPServerStdio, MCPServerStreamableHttp, MCPServerSse) verfügbar, falls du es brauchst:
# No-run: illustrative Agents SDK shape; requires openai-agents, a configured hosted MCP server, and credentials.
from agents import Agent, HostedMCPTool
def approve(request):
# Only tools under the "always" policy reach this callback.
if request.data.name == "delete_repo":
return {"approve": False, "reason": "escalate to a human reviewer"}
return {"approve": True}
agent = Agent(
name="Ops",
tools=[HostedMCPTool(
tool_config={
"type": "mcp",
"server_label": "github",
"server_url": "https://mcp.example.com",
"require_approval": {
"always": {"tool_names": ["delete_repo"]},
"never": {"tool_names": ["list_issues"]},
},
},
on_approval_request=approve,
)],
)
Der Approval Callback ist Code. Die Approval Policy pro Tool ist Code. Du kannst diese Datei lesen, testen und diffen. Nichts davon gilt für einen System Prompt, der sagt: „Bitte sei in Production vorsichtig.“
Codex CLI und die Managed Policy Layer
OpenAIs Coding Harness unterstützt eine Managed-requirements.toml-Datei, die IT-Abteilungen über Device Management verteilen können. Auf Unix-Systemen liegt die Systemdatei unter /etc/codex/requirements.toml. Sie fungiert als Hard-Constraint-Layer, sodass Einstellungen auf Projektebene ihre Regeln nicht überschreiben können:
# /etc/codex/requirements.toml
[rules]
prefix_rules = [
{ pattern = [{ token = "rm" }, { any_of = ["-rf", "-fr"] }], decision = "forbidden", justification = "Recursive force-delete prohibited by IT policy" },
]
prefix_rules.decision akzeptiert nur "prompt" oder "forbidden", niemals "allow". Ein Projekt kann sich keine Berechtigung geben, die der Managed Layer verbietet. MCP-Allowlists sind sowohl an Name als auch an Identity gebunden, etwa an einen Command String oder eine URL. Ein Projekt kann daher nicht behaupten, github-mcp zu sein, und auf den Server eines Angreifers zeigen. Die unterstützten Anforderungen variieren je nach Client und Version. Die aktuelle Dokumentation verlangt für Managed Permission-Profile-Keys ausdrücklich Codex 0.138.0 oder höher. Teste daher jede Requirements Policy vor dem Rollout gegen alle Client-Versionen in der Flotte.
Claudes Code Permission Ladder
Claude Code veröffentlicht keine feste Sequenz aus sechs Checks für jeden Tool Call. Seine Permission Rules werden deny → ask → allow ausgewertet; die erste passende Regel bestimmt das Ergebnis. Ein PreToolUse-Hook läuft vor dem Permission Prompt. Ein Hook kann einen Call blockieren, aber ein Hook-Ergebnis umgeht keine passende Deny- oder Ask-Regel. Der aktive Permission Mode behandelt Calls, die von den Rules nicht aufgelöst werden. Das Claude Agent SDK besitzt einen separaten canUseTool-Callback für ungelöste Requests. Dieser Callback ist eine SDK-Control, kein Permission Check der Claude Code CLI.
Modes wechseln mit Shift+Tab zwischen default → acceptEdits → plan. auto, bypassPermissions und dontAsk werden unter bestimmten Entry Conditions aktiviert, die die Enterprise-Managed-Policy-Layer sperren kann. Hier wird also nicht einfach eine Config-Datei auf Korrektheit geprüft. Es handelt sich um eine State Machine mit Precedence Rules, die so veröffentlicht ist, dass ein Security-Team sie nachvollziehen kann.
Drei Blast Radii in einer Datei
So sieht die Struktur einer Codex-artigen Permission Config mit einem Default und zwei benannten Profilen aus:
# ~/.codex/config.toml
approval_policy = "on-request"
sandbox_mode = "workspace-write"
[profiles.ci]
approval_policy = "never"
sandbox_mode = "read-only"
[profiles.release]
approval_policy = "untrusted"
sandbox_mode = "danger-full-access"
[mcp_servers.github]
command = "gh-mcp"
args = ["--readonly"]
Zwei Keys leisten die eigentliche Arbeit, und sie sind unabhängig voneinander. approval_policy entscheidet, wann ein Mensch gefragt wird. on-request erlaubt dem Agenten die Eskalation, wenn er nicht weiterkommt. never fragt überhaupt nicht. untrusted stoppt jeden Befehl, der nicht auf der Trusted List steht. sandbox_mode bestimmt, worauf der Befehl zugreifen kann, wenn er ausgeführt wird.
CI unterbricht niemanden und darf nicht schreiben. Release kann die gesamte Maschine erreichen, muss aber fast alles zuerst mit einem Menschen klären. Das Release-Profil bezahlt für diese Reichweite: danger-full-access schaltet die Sandbox aus, sodass nur noch die untrusted-Genehmigung als Kontrolle verbleibt. Alles außerhalb der Trusted List wird von einem Menschen freigegeben oder nicht ausgeführt. Diese Trusted List ist nun die gesamte Security Boundary.
Die Default- und CI-Profile behalten den Kernel darunter: Seatbelt auf macOS, bubblewrap plus seccomp auf Linux und Restricted Tokens auf Windows. In jedem Fall spielt die Meinung des Models keine Rolle.
Sandbox Enforcement ist eine OS-Frage
Der Kernel erledigt hier die eigentliche Arbeit. Jedes OS stellt ein anderes Toolkit bereit, und die beiden CLIs greifen nicht immer auf dasselbe Element zurück:
| Plattform | Claude Code | Codex CLI |
|---|---|---|
| macOS | Seatbelt über sandbox-exec mit einem SBPL-(Seatbelt Profile Language)-Profil | Seatbelt über sandbox-exec -p |
| Linux | bubblewrap + socat Network Proxy | bubblewrap + seccomp (Legacy-Landlock über use_legacy_landlock) |
| Windows | WSL2 erforderlich | Native Restricted Tokens + Workspace ACLs + Capability SIDs |
Sie stimmen dort überein, wo das OS eine Option vorgibt (Seatbelt, bubblewrap), und unterscheiden sich dort, wo dies nicht der Fall ist. Claudes Code Sandbox erfordert unter Windows WSL2; das ist eine Sandbox-Limitation, keine Aussage darüber, dass die CLI nicht nativ laufen kann. Codex liefert eine native Windows-Sandbox. In jedem Fall findet Enforcement im Kernel statt, nicht im Model.
Codex’ Linux-Pfad legt drei Locks auf Kernel-Ebene um den Befehl. PR_SET_NO_NEW_PRIVS verhindert, dass der Prozess zusätzliche Privileges erlangt, selbst wenn er es versucht. Ein seccomp-Filter veranlasst den Kernel, ganze Klassen von System Calls abzulehnen. Die Network Restrictions hängen davon ab, ob Networking deaktiviert oder ein Proxy Mode konfiguriert ist; sie sind nicht universell auf Unix-Sockets beschränkt. Siehe die Linux-Sandbox-Implementierung. Ein frischer isolierter /proc verbirgt den Rest der Maschine.
Codex härtet außerdem sein eigenes Binary beim Start auf jeder Unix-Plattform. Es setzt RLIMIT_CORE=0, um Crash Dumps zu unterdrücken, und verweigert Debugger Attach. Das ist eine andere Boundary als die Sandbox.
Die Windows-Sandbox besitzt zwei Modi. unelevated verwendet den User-seitig einen Restricted Token und bietet nicht die Network Enforcement des erhöhten Modus. elevated verwendet dedizierte Sandbox User, Firewall Rules und Filesystem ACLs.
Wenn Network Access deaktiviert ist, legt Codex Stub-.bat- und .cmd-Dateien für ssh und scp in einem Verzeichnis am Anfang von PATH ab. Diese Commands beenden sich mit einem Non-Zero-Status, statt die echten Binaries zu erreichen. Codex setzt außerdem HTTP_PROXY, HTTPS_PROXY, ALL_PROXY und die Git-Proxy-Variablen auf einen nicht erreichbaren lokalen Port. Diese Maßnahmen betreffen kooperative Tools; Proxy-Variablen und Command Stubs allein können beliebiges Networking nicht einschränken.
Eine Sandbox braucht außerdem einen definierten Failure Mode. In Claudes aktueller Code-Konfiguration stoppt sandbox.failIfUnavailable: true die Execution, wenn die Sandbox nicht gestartet werden kann. allowUnsandboxedCommands: false deaktiviert den Unsandboxed-Retry-Escape-Hatch des Agents, aber excludedCommands umgeht ihn weiterhin. Teste die effektive Konfiguration mit fehlenden Dependencies und einem verbotenen Ziel.
Credential Brokering ist jetzt auch lokal verfügbar. Claudes Code sandbox.credentials kann benannte Dateien und Environment Variables verweigern. Environment-Variable-Masking, verfügbar seit v2.1.199, gibt sandboxed Commands einen Placeholder und lässt den Proxy den echten Wert in Requests an konfigurierte Hosts injizieren. Setze enge injectHosts und konfiguriere TLS Termination; Masking autorisiert die angeforderte Operation nicht. Diese Kontrollen decken sandboxed Bash Commands ab. Hooks, MCP-Prozesse und andere Execution Paths benötigen daher weiterhin ihre eigene Credential Policy.
Isolation-Optionen jenseits von Claude Code und Codex
Wenn du einen eigenen Agenten entwickelst, erweist sich „Sandbox“ als Sammelbegriff. Die Open-Source-Optionen liegen auf einem Spektrum – von leichtgewichtigen Namespace Wrappers bis hin zu vollständigen MicroVMs – und die Wahl hängt davon ab, wie sehr du dem darin ausgeführten Code vertraust.
Leichte Isolation – gleicher Kernel, weniger Berechtigungen:
- bubblewrap – ein Low-Level-Namespace-Sandbox-Constructor, der von Flatpak und Claude Code unter Linux verwendet wird. Der Caller muss Filesystem, Netzwerk und eine optionale seccomp Policy festlegen; bubblewrap selbst ist keine fertige Security Policy.
- Standard-Docker-/OCI-Container – Namespace-Isolation über einen gemeinsam genutzten Host-Kernel. Keine Sandbox für nicht vertrauenswürdigen Code; die eigenen Docs von gVisor stellen dies klar („containers are not a sandbox“). Als Ausgangspunkt in Kombination mit seccomp und AppArmor sinnvoll, mehr aber nicht.
Application-Kernel-Isolation – der Agent spricht mit einem Fake-Kernel:
- gVisor – Googles User-Space-Kernel. Dein Container glaubt, unter Linux zu laufen, während eine in Go implementierte Kernel-Schicht System Calls abfängt. Das reduziert die direkte Exposure des Host-Kernels ohne Guest-VM, bringt jedoch Kompatibilitäts- und Performance-Trade-offs mit sich.
Vollständige VM-Isolation – ein dedizierter Kernel pro Sandbox:
- Firecracker – AWS’ MicroVM-Technologie. Jede VM besitzt unter KVM ihren eigenen Linux-Kernel; Container teilen sich den Host-Kernel. Ein Compromise in einer VM muss weiterhin VMM- oder Host-Kontrollen überwinden, um den Host oder eine andere VM zu beeinflussen. Firecrackers Jailer und ein gepatchter Host bleiben daher Teil des Schutzes.
- Kata Containers – Container-UX mit VM-Grade-Isolation. Die Option für Kubernetes-Cluster, die nicht vertrauenswürdigen Code ausführen müssen.
Plattformen – was du stattdessen mieten würdest:
- E2B verpackt Firecracker in eine gehostete Sandbox API.
- OpenSandbox trennt das SDK von der durch Administratoren konfigurierten Runtime-Isolation. Default-Docker-runc ist keine MicroVM; der Firecracker-Pfad verwendet Kata mit Firecracker über Kubernetes.
- Microsofts Agent Governance Toolkit (MIT-lizenziert, April 2026) ergänzt eine Runtime Policy Engine. Sie ordnet Policy Controls der OWASP ASI Top 10 zu. Die Angabe zur Launch-Latency bezieht sich auf eine einzelne Policy Engine, nicht auf die Gesamtkosten aller Checks in einem eingesetzten Agenten.
Wähle das Isolationsniveau anhand des Trust Levels des Codes, der Tenant Boundary, des Network Access, der Host-Daten und der Recovery-Kosten. Namespace- und seccomp-Kontrollen können für vertrauenswürdige interne Tools ausreichen. LLM-generierter Code und nicht vertrauenswürdige Packages benötigen eine stärkere Boundary wie gVisor, Kata oder eine MicroVM, gefolgt von Tests gegen Escape- und Exfiltration-Pfade in deinem eigenen Threat Model.
Claude Code und Codex haben aus demselben Menü gewählt wie alle anderen. Sie haben es nur unterschiedlich verpackt.
PreToolUse-Hooks als programmierbare Policy
Modes und Allowlists decken einfache Fälle ab: „Lass den Agenten Dateien bearbeiten, aber kein Bash ausführen“, „Verweigere alles, was wie rm -rf aussieht.“ Sie versagen, wenn deine Policy echte Logik benötigt. Du willst git push nur blockieren, wenn der Branch main ist. Du willst jedes Edit verweigern, das eine Datei verändert, die einem Secret-Regex entspricht. Du willst Shell Calls pro Session rate-limitieren oder jeden Tool Invocation in dein zentrales Audit Log pipen (das SIEM, also das Security Information and Event Management System, das dein Security-Team bereits überwacht).
Nichts davon passt in eine statische Allowlist. Dafür gibt es Hooks – Shell Commands, die Claude Code an bestimmten Punkten im Tool-Call-Lifecycle ausführt und die den ausstehenden Call prüfen sowie ein strukturiertes Allow/Deny zurückgeben können. Claude Code stellt etwa dreißig Lifecycle Events bereit (die vollständige Liste steht in der Dokumentation); eines davon ordnet alles andere neu: Ein PreToolUse-Hook, der permissionDecision: "deny" zurückgibt, blockiert ein Tool unabhängig vom Mode.
So sieht die Settings-Struktur aus:
{
"permissions": {
"defaultMode": "acceptEdits",
"deny": ["Bash(rm -rf:*)", "Bash(sudo:*)", "Read(.env*)"]
},
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/pre-bash-firewall.sh"
}
]
},
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/protect-paths.sh"
}
]
}
]
}
}
Die statischen deny-Regeln und die dynamischen Hooks haben unterschiedliche Failure Modes. Ein PreToolUse-Ergebnis von deny blockiert vor dem normalen Permission Flow, aber ein Timeout bei einem command-, HTTP- oder MCP-Tool-Hook ist Non-Blocking: Claude Code setzt diesen Flow fort. In diesem Beispiel kann acceptEdits daher ein Edit oder Write genehmigen, wenn protect-paths.sh ein Timeout erreicht. Lass eine nicht verhandelbare Path Restriction nicht ausschließlich von einem Command Hook abhängen. Lege statische Restriktionen in Deny Rules oder der Sandbox ab. Für dynamische Policy solltest du eine Control wählen, deren Evaluator Failure restriktiv bleibt, und sowohl das Timeout-Verhalten als auch ein explizites Deny testen.
Ein Hook kann ein Shell Script mit fünf Zeilen oder eine vollständige Policy Engine sein. Entscheidend ist die Return-Struktur:
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "writes outside workspace prohibited"
}
}
Das Model sieht ein strukturiertes Deny. Der Reasoning Loop aus Teil 1 behandelt es wie jede andere Tool Observation: Die Verweigerung wird zu Context, der Agent plant neu, der Loop läuft weiter. Genau das bringt „Berechtigungen sind Infrastruktur“. Das Deny ist in denselben Mechanismus eingebunden, der auch einen 500er von einem HTTP Tool verarbeitet. Es ist kein separater Security Workflow, der nachträglich angebaut werden muss.
Ein häufiges Anti-Pattern besteht darin, einen System Prompt zu schreiben, der sagt „Lösche keine Dateien ohne explizite User-Bestätigung“, den Agenten auszuliefern und diese Anweisung als Control zu verwenden. Ein Injected Prompt oder ein Tool Result, das ein Angreifer kontrolliert, kann diese Anweisung umgehen. Das Model ist keine Policy Engine. Es kann das von dir geschriebene Muster oder ein vom Angreifer geliefertes Muster erkennen.
Human Approval funktioniert nur als Eskalation
Content Filter prüfen, was das Model sagt. Permission Rules prüfen den Tool Call, bevor er ausgeführt wird. Human-in-the-Loop-Review übernimmt die Aktionen, für die weiterhin ein Mensch erforderlich ist. Wenn Menschen 93 % der Prompts genehmigen, prüfe die Eskalationsregeln und die Qualität der Prompts.
LangGraph liefert das Pause-/Resume-Primitive. HumanLayer verpackt den Approval Channel, und Anthropics Nutzungsdaten zeigen, warum Anzahl und Qualität der Eskalationen gemessen werden müssen.
Das LangGraph-Primitive
LangGraphs interrupt() + Command(resume=value) pausiert einen Graphen, persistiert seinen Zustand über den konfigurierten Checkpointer und setzt ihn mit einem von Menschen gelieferten Wert fort. Ob dieses Resume sicher ist, hängt von einem Detail in der Dokumentation ab:
„Wenn die Execution fortgesetzt wird (nachdem du den angeforderten Input geliefert hast), startet die Runtime den gesamten Node von Anfang an neu – sie setzt nicht exakt in der Zeile fort, in der
interruptaufgerufen wurde.“
Aus diesem Restart-Verhalten folgen drei Einschränkungen:
1. Side Effects vor interrupt() müssen idempotent sein. Wenn der Mensch antwortet, läuft der gesamte Node von oben erneut, nicht ab der Zeile interrupt(). Wenn dein Node also eine E-Mail sendet, auf Approval wartet und dann „gesendet“ zurückgibt, wird die E-Mail beim Resume ein zweites Mal gesendet. Lösung: Platziere Side Effects nach dem Interrupt oder mache sie wiederholbar sicher (Dedupe Keys, Upsert statt Insert, Cache nach Message ID).
2. Interrupts werden nach Index, nicht nach Name, Resumes zugeordnet. Wenn ein einzelner Node zwei interrupt()-Aufrufe enthält, ordnet LangGraph sie den Command(resume=...)-Werten in der Reihenfolge ihres Auftretens zu. Jede Verzweigung, die verändert, wie viele Interrupts ausgeführt werden (ein if, das beim Resume einen überspringt, oder eine Schleife mit einer anderen Iterationszahl), verschiebt die Indizes. Dadurch kann ein Resume-Wert beim falschen Interrupt landen.
3. Halte Payloads JSON-sicher. LangGraphs Dokumentation verlangt JSON-serialisierbare Werte für interrupt() und Resume-Payloads. Verwende Strings, Zahlen, Booleans, Arrays und Dictionaries, die solche Werte enthalten. Vermeide Functions, Class Instances und andere komplexe Objekte, da die Serialisierung vom konfigurierten Checkpointer abhängt. Wandle Approval-Daten in Dictionaries und Primitives um, bevor du sie an interrupt() übergibst oder über eine HTTP API veröffentlichst.
Die drei kanonischen Patterns:
# No-run: illustrative LangGraph sketches; requires LangGraph, a tool decorator, interrupt, and smtp_send.
# (a) Approval check
@tool
def send_email(to, subject, body):
resp = interrupt({"action": "send_email", "to": to,
"subject": subject, "body": body})
if resp.get("action") == "approve":
return smtp_send(to, subject, body)
return "Email cancelled"
# (b) Edit-and-continue
def review_node(state):
edited = interrupt({"content": state["generated_text"]})
return {"generated_text": edited}
# (c) Mid-run state correction — conditional edge
class AgeState(TypedDict):
age: int | None
pending_question: str | None
def get_age_node(state: AgeState):
question = state.get("pending_question") or "What is your age?"
answer = interrupt(question) # once per node invocation
if isinstance(answer, int) and answer > 0:
return {"age": answer, "pending_question": None}
return {"pending_question": f"'{answer}' is not valid. Please enter a positive number."}
def route_age(state: AgeState):
return END if state.get("age") is not None else "get_age"
builder = StateGraph(AgeState)
builder.add_node("get_age", get_age_node)
builder.add_edge(START, "get_age")
builder.add_conditional_edges("get_age", route_age)
Resume ist graph.invoke(Command(resume={"action": "approve"}), config=cfg). LangGraph 0.4+ unterstützt ein Dict-basiertes Multi-Interrupt-Resume für parallele Branches – relevant ab dem Moment, in dem dein Agent fächert.
HumanLayer: Approval als Produkt
HumanLayer ist die Managed-Version derselben Idee. Du dekorierst eine Function, und Approval Requests werden an Slack, E-Mail oder Discord geleitet, mit Regeln dafür, wer benachrichtigt wird. Wenn der Agent versucht, multiply(2, 5) aufzurufen, sehen die Logs so aus:
last message led to 1 tool calls: [('multiply', '{"x":2,"y":5}')]
HumanLayer: waiting for approval for multiply
Der Approver klickt in Slack auf Approve oder Deny. Bei einem Deny formuliert es die HumanLayer-Dokumentation so: „HumanLayer gibt dein Feedback an den Agenten zurück, der daraufhin seinen Ansatz anpassen kann.“ Dieses Feedback erlaubt dem Agenten, seinen Plan zu überarbeiten, statt eine Ablehnung als Sackgasse zu behandeln.
Approval Fatigue in den Daten
Anthropic veröffentlichte die realen Daten im Februar 2026. Drei Ergebnisse sind wichtiger als die übrigen.
„Wir haben festgestellt, dass 80 % der Tool Calls von Agents stammen, die offenbar mindestens eine Art von Safeguard besitzen (etwa eingeschränkte Berechtigungen oder Anforderungen an Human Approval), 73 % offenbar in irgendeiner Form einen Menschen im Loop haben und nur 0,8 % der Actions offenbar irreversibel sind.“
Das sind gute Nachrichten. Betrachte 80 % als obere Grenze, denn Anthropics Fußnote 14 ergänzt: „Claude überschätzte die menschliche Beteiligung häufig, daher erwarten wir, dass 80 % eine obere Grenze darstellen.“
„Neuere User (<50 Sessions) verwenden Full Auto-Approve ungefähr in 20 % der Fälle; bei 750 Sessions steigt dies auf über 40 % der Sessions.“
Das ist der Drift. User beginnen vorsichtig und werden weniger vorsichtig, während sie Vertrauen in das Tool aufbauen. Das ist menschlich und kein Charakterfehler. Es ist ein Telemetrie-Signal, das dein System erfassen sollte. (Ein kleiner Fact-Check-Hinweis: In sekundärer Berichterstattung wurde dies häufig als „20 % → über 50 %“ zitiert. Laut Anthropics Primärdaten lautet die verifizierte Zahl 20 % → über 40 %. Falls du die 50-%-Zahl gesehen hast: Daher stammt sie.)
Anthropics Engineering-Post vom März 2026 zu Claudes Code Auto Mode nennt die entscheidende Zahl:
„Claude-Code-User genehmigen 93 % der Permission Prompts. Wir haben Classifier entwickelt, um einige Entscheidungen zu automatisieren, die Safety zu erhöhen und gleichzeitig Approval Fatigue zu reduzieren … Wenn eine Session drei aufeinanderfolgende Denials oder insgesamt 20 Denials ansammelt, stoppen wir das Model und eskalieren an den Menschen.“
Eine hohe Approval Rate ist ein Anlass, die Frage zu prüfen, ob Prompts Menschen bei der Entscheidung helfen; sie beweist keinen universellen Failure Threshold. Anthropics zweistufiger Classifier reduziert Prompts und eskaliert gebündelte Denials. Die gemeldete False-Positive-Rate von 0,4 % wurde mit 10.000 Traffic-Beispielen ermittelt. False-Negative-Rates lagen bei 17 % für 52 reale, übereifrige Actions und 5,7 % für 1.000 synthetische Exfiltration Cases. Das sind unterschiedliche Populationen, keine ergänzenden Prozentsätze und kein Beleg dafür, dass Classification Sandboxing ersetzt.
Qualität der Eskalation messen
Nimm routinemäßige, reversible Actions in die Allowlist auf und logge sie. Eskaliere Actions, deren Side Effects eine Boundary überschreiten, die die Runtime nicht rückgängig machen kann, etwa eine externe Nachricht, einen Production Write, einen Force Push oder eine Zahlung. Anthropic formuliert das Ziel so, dass ein Mensch eingreifen kann, wenn die Entscheidung echte Konsequenzen hat.
Verfolge den vollständigen Funnel, statt ein fremdes Approval-Rate-Ziel anzustreben: vorgeschlagene Actions, automatische Allows, Eskalationen, Approvals, Denials, Edits und Incidents nach einem Approval. Eine hohe Approval Rate kann bedeuten, dass die Prompts routinemäßiges Rauschen sind. Eine hohe Denial- oder Edit-Rate kann bedeuten, dass der Planner die falsche Aktion vorschlägt oder die für den Approver benötigten Informationen verbirgt. Der sinnvolle Threshold hängt von der Action-Klasse und den Kosten eines False Allow ab. Leite ihn daher aus deinen eigenen Incident- und Review-Daten ab.
MCP-Scoping und die Supply Chain
MCP verbindet Agents mit externen Tools wie Slack, GitHub und Datenbanken, wodurch sein Authorization Model Teil der Security Boundary wird. Die Spec-Revisions von 2025 trennten die Rollen von Token Issuer und Resource Server und fügten Resource Indicators hinzu. Diese Historie erklärt, welche Audience- und Forwarding-Checks ein Server heute erzwingen muss.
MCP Authorization in drei Revisionen
Authorization war für MCP-Implementierungen in der Spec vom 26.03.2025 optional. Für ein Production-HTTP-Deployment, das User-Daten oder Tools schützt, empfehle ich OAuth 2.1 mit PKCE (Proof Key for Code Exchange), das die Spec verlangt, sobald eine Implementierung OAuth Authorization unterstützt. Das frühe Design erlaubte einem MCP Server, zwei Rollen zu übernehmen. Der Authorization Server stellt Tokens aus; der Resource Server akzeptiert sie. Das sind getrennte Rollen, selbst wenn ein Service beide ausführt. Wenn dieser Service einen Request an einen anderen Server weiterleitet, kann dasselbe Credential an einen Ort gelangen, für den es nicht bestimmt war. Das ist die Lücke.
Die Revision vom 18.06.2025 machte die Rollen explizit. Ein geschützter MCP Server fungiert als OAuth Resource Server, während ein Authorization Server das Token ausstellt. Der Authorization Server kann gemeinsam mit dem Resource Server gehostet oder separat betrieben werden. RFC 8707 Resource Indicators binden das Token an eine Zielressource, und RFC 9728 Protected Resource Metadata gibt dem Client einen expliziten Discovery Path. Die Spec verbietet einem MCP Server außerdem, das Token eines Clients upstream weiterzuleiten.
Die Revision vom 25.11.2025 behielt diese Trennung bei und arbeitete an den Teilen, die ein Client korrekt umsetzen muss. Die Discovery des Authorization Servers erhielt OpenID Connect Discovery, sodass ein Client den richtigen Issuer finden kann, statt zu raten. Incremental Scope Consent wurde in den Header WWW-Authenticate verschoben. Dadurch kann ein Server genau dann einen weiteren Scope anfordern, wenn er ihn benötigt, statt von Anfang an alles zu verlangen. Die Client Registration erhielt OAuth Client ID Metadata Documents als empfohlenen Mechanismus und ersetzte damit Dynamic Registration für die meisten Deployments. Auch die Discovery von Protected Resource Metadata wurde an RFC 9728 angeglichen, wodurch WWW-Authenticate optional und ein .well-known-Fallback möglich wurde.
Prüfe vor der Implementierung die Versionierungsseite. Am 6. September 2026 ist die aktuelle Revision 2026-07-28. Sie verlangt, dass jeder Request die Protocol Version deklariert, und erlaubt dem Server, jeden Request unabhängig zu akzeptieren oder abzulehnen. Ein Client kann vorab server/discover aufrufen, um eine Version auszuwählen, aber Discovery ist optional. Declaration und Negotiation pro Request bleiben erforderlich – auch dann, wenn der Client einen Unsupported-Version-Fehler behandelt und mit einer gemeinsam unterstützten Version erneut versucht.
Audience Binding begrenzt Replay gegen den falschen MCP Server. Es neutralisiert nicht die oben beschriebenen separaten Claude-Code-Konfigurationsschwachstellen: Ein Host-seitiger Hook kann weiterhin vor dem Start des Models ausgeführt werden, und ein nicht vertrauenswürdiges Projekt kann weiterhin versuchen, die lokale Konfiguration zu ändern. Token Scope, Project Trust, Hook Policy und Sandboxing bleiben separate Controls.
Die MCP-Checkliste für 2026
Wenn du MCP in Production bereitstellst oder konsumierst:
- Behandle Authentication als Production Requirement, nicht als Protocol Default. MCP lässt Authorization optional, aber ich empfehle OAuth 2.1 mit PKCE für ein geschütztes HTTP-Deployment. Das Advisory zum gehosteten Azure Web Apps MCP Service betraf fehlende Authentication. Wenn dein Server Traffic akzeptiert, ohne Caller Credentials zu verifizieren, hast du ein Tool gebaut, das jeder aufrufen kann, der es erreicht.
- Tokens sind Audience-Bound. Fordere ein Token für die Zielressource des MCP an und validiere, dass das präsentierte Token deinen Server als Audience nennt. Lehne Tokens ab, die für eine andere Ressource ausgestellt wurden.
- Trenne Read- und Write-Authority bewusst. MCP bindet ein Token an einen Resource Server, nicht an ein einzelnes Tool. Wenn ein Slack Server ein Credential mit
chat:writeakzeptiert und es an Read- und Write-Handler routet, kann ein Read-orientiertes Tool über die Policy dieses Servers zu einem Message-Sending-Pfad werden. Verwende separate Resource Server oder separate Credentials und Authorization Checks, wenn Read- und Write-Operationen unabhängige Blast Radii benötigen. - Verwende frische, kurzlebige Tokens statt dauerhafter API Keys. Das Claude-Managed-Agents-Vault-Pattern (Anthropic Engineering) ist die Referenz: Der Agent selbst sieht die echten Credentials nie. Der Proxy ruft die entsprechenden gespeicherten Credentials aus dem Vault ab, ruft das Tool im Namen des Agents auf und gibt das Ergebnis zurück. Ein frisches Token für jeden Call ist keine dokumentierte Garantie; kurzlebige Credentials sind eine Deployment-Empfehlung.
Supply-Chain-Kontrollen gelten weiterhin
Die Axios- und Trivy-Incidents sind bekannte Package- und CI-Supply-Chain-Fehler, angewandt auf Systeme, die Dependency Installation automatisieren. Automation erhöht Anzahl und Geschwindigkeit der Ausführungen. Deshalb müssen Version-, Provenance- und Review-Kontrollen greifen, bevor der generierte Befehl CI oder eine Sandbox erreicht.
Die Abwehr ist unkompliziert:
- Pinne Versionen in der Lockfile. Agents dürfen niemals eine Floating Version auflösen – kein
@latest, keinnpm update, kein--upgrade. - Scanne in CI mit Tools, die unabhängig von der geprüften Komponente sind.
- Verwende GitHub Commit SHAs für Actions, nicht Tags.
- Prüfe Dependency Diffs in Agent-gesteuerten PRs vor dem Merge.
Das sind Standard-Supply-Chain-Kontrollen. Agent Automation verändert ihre Frequenz, nicht ihren Mechanismus.
Ein Policy-Stack für den Market Analyst Agent
Der Market Analyst Agent aus Teil 1 ist ein kleiner LangGraph-Agent, der Market Data abruft und einen Analyst Report schreibt – aber nicht so klein, wie diese Beschreibung vermuten lässt. Neben den Market-Data-Tools führt er eine Allowlisted CLI über subprocess aus, evaluiert vom Model geschriebenes Python im Prozess und erstellt simulierte Trade Records. Er nutzt Tool Invocation, Code Execution und Approval Routing, platziert aber keine echten Orders. So sieht ein minimaler Policy-Stack dafür aus.
Layer 1: Ein PreToolUse-Hook, der vor der Execution verweigert
Selbst ein Agent, der „nur Aktiendaten liest“, kann Dinge aufrufen, die er nicht sollte: ein curl an eine vom Angreifer kontrollierte URL, Writes außerhalb des Workspace, git-Mutations im Host-Repository. Eine Deny Rule ist Infrastruktur, kein Prompt. Die folgende Skizze gibt die eigene Decision-Struktur des Agents zurück, nicht den umschließenden hookSpecificOutput-Envelope, den Claude Code erwartet.
# agent/permissions.py
from pathlib import Path
DENY_COMMANDS = frozenset({
"rm -rf", "sudo", "chmod 777",
"curl -X POST", "wget", "nc ",
})
WORKSPACE = Path("./workspace").resolve()
def _outside_workspace(path: str) -> bool:
# Resolve first: "~/.ssh/id_rsa" and "workspace/../../etc" both
# have to become real paths before the comparison means anything.
return not Path(path).expanduser().resolve().is_relative_to(WORKSPACE)
def pre_tool_use(tool_name: str, args: dict) -> dict | None:
if tool_name == "shell":
cmd = args.get("command", "")
if any(bad in cmd for bad in DENY_COMMANDS):
return {"permissionDecision": "deny",
"reason": f"command pattern disallowed: {cmd!r}"}
if tool_name == "write_file":
path = args.get("path", "")
if _outside_workspace(path):
return {"permissionDecision": "deny",
"reason": f"path outside workspace: {path!r}"}
return None # fall through to mode / canUseTool
Die Skizze macht den Control Point sichtbar. Der Hook gibt ein strukturiertes Deny zurück, und der Reasoning Loop erhält diese Denial als Tool Observation.
Der Path Check ist eine Allowlist: ein Workspace Root, alles andere wird verweigert. Eine Denylist verbotener Prefixes blockiert nur Pfade, an die du gedacht hast. ~/.ssh/id_rsa wird niemals exakt so geschrieben, wie du es notiert hast. Der Command Check bleibt eine Denylist. Substring Matching ist keine Production-Shell-Policy. Eine echte Implementierung sollte den Command parsen und bei der Execution auf die OS-Sandbox setzen. Die Skizze ist selbst keine Execution Boundary: Wenn ein externer Hook sie ausführt, muss ein Timeout eine unabhängige Workspace Restriction aktiv lassen.
Layer 2: Ein Input Canary gegen Prompt Injection
Agent-Goal-Hijacking (ASI01) kommt häufig über eine Retrieved Web Page, eine User Message oder ein Research-Paper-PDF. Ein einfacher Regex-Canary erkennt wörtliche Instruction Patterns und erzeugt ein nützliches Telemetry Event. Obfuscated, mehrsprachige und kontextabhängige Injections wird er übersehen; daher kann er nicht als Decision Boundary dienen:
# agent/input_canary.py
import re
INJECTION_PATTERNS = [
re.compile(r"ignore\s+(?:all\s+|any\s+|the\s+)?"
r"(?:previous\s+|prior\s+|above\s+|earlier\s+)?"
r"(?:instructions|rules|prompts?)",
re.IGNORECASE),
re.compile(r"you are now|act as|roleplay as", re.IGNORECASE),
re.compile(r"system[ _:]*prompt", re.IGNORECASE),
re.compile(r"<\|im_(start|end)\|>"),
]
def input_canary(text: str) -> dict | None:
for pat in INJECTION_PATTERNS:
m = pat.search(text)
if m:
return {"flag": "possible_injection", "match": m.group(0)}
return None
Logge markierte Inputs; lehne sie nicht automatisch ab. False Positives sind für einen Research Assistant teuer. Aber das Log ermöglicht es dir zu erkennen, wenn die Anzahl der Flags bei einem User plötzlich ansteigt.
Layer 3: Structured-Output-Validation über einen Stop-Hook
Ein Pydantic Model plus ein Stop-Hook liefert für die Report-Generierung einen engen Validate-then-Retry-Loop. Der Agent kann nicht „done“ melden, bevor der Output die Schema-Validation und einen Smoke Test besteht:
# No-run: illustrative policy sketch; requires Pydantic and the repo-local agent.schemas module.
# agent/stop_hook.py
from pydantic import ValidationError
from agent.schemas import MarketReport
def on_stop(final_output: str) -> dict:
try:
report = MarketReport.model_validate_json(final_output)
except ValidationError as e:
return {"decision": "continue",
"feedback": f"schema invalid: {e.errors()[:3]}"}
if not report.tickers:
return {"decision": "continue",
"feedback": "no tickers in report — did you skip the snapshot step?"}
return {"decision": "allow_stop"}
Ein Schema Check und ein Smoke Test machen den Unterschied zwischen „der Agent hat gesagt, er sei fertig“ und „der Output ist tatsächlich ein Report“.
Layer 4: Approval vor Outbound Actions
Das execute_trade des Market Analyst ist eine simulierte, idempotente State Transition. Es demonstriert daher Approval Routing und keinen irreversiblen Financial Side Effect. Bei einer echten Outbound Integration – E-Mail, Slack, Report an einen Kunden oder Brokerage Order – zeige dem Menschen die vorgeschlagene Action und warte auf Approval oder Rejection, bevor das Tool ausgeführt wird. Verwende interrupt() für diese Pause:
# No-run: illustrative outbound-tool sketch; requires LangGraph, a tool decorator, and smtp_send.
# agent/tools/notify.py
from langgraph.types import interrupt
@tool
def send_report(to: str, body: str):
resp = interrupt({
"action": "send_report",
"to": to,
"body": body, # Review the complete content that will execute.
})
if resp.get("action") == "approve":
return smtp_send(to, body)
return "send cancelled by human"
Outbound Actions vervollständigen das lethal trifecta. Fordere Approval für Outbound Actions, wenn die vorhandene Authorization und Deployment Policy sie nicht abdecken. Binde das Approval an vollständiges Ziel und vollständigen Content; geänderte Arguments erfordern eine neue Entscheidung. Messages an Finance, Kunden oder andere externe Empfänger sollten dem Approver zeigen, was gesendet wird und wohin.
Was dieser Stack nicht leistet
Dies ist keine Abwehr gegen:
- Eine kompromittierte Upstream-Dependency (Axios-Klasse). Der Agent führt aus, was
uv syncvorgibt. - Einen bösartigen
.mcp.jsonin einem geklonten Repository (CVE-2025-59536-Klasse). Das Permission Model des Host-MCP-Clients ist der Ort, an dem dies abgefangen wird, nicht der Code des Agents. - Eine Data-Theft-Chain aus legitimen Tools (EchoLeak-Klasse) – der Agent liest private Daten, ruft externe URLs ab und versendet Messages. Unterbrich oder beschränke diesen Exfiltration Path durch gescopten Data Access, Trusted Routing, Egress Restrictions und erforderliches Approval. Das Entfernen einer Capability blockiert diesen konkreten Pfad, nicht jeden möglichen Angriff.
- Einen Escape aus
execute_python_analysis, dem In-Process-Python-Evaluator des Agents. Er blockiert eine Liste von Statement Types, weist jeden Identifier zurück, der mit einem Underscore beginnt, und erlaubt Imports nur ausjson,mathundstatistics. Aberexecim Worker-Prozess ist keine Boundary: Ein Bypass läuft mit den File Handles und dem Netzwerkzugriff des Workers. Verschiebe ihn hinter Filesystem-, Network- und Credential-Isolation sowie CPU-, Memory- und Time-Limits, bevor du nicht vertrauenswürdigen Code evaluierst. Ein alleiniger Subprocess übernimmt Berechtigungen und ist keine Security Sandbox.
Diese vier Layer sind lokale Policy, und lokale Policy ist die innerste von dir kontrollierte Schicht, nicht die einzige. Jeder Punkt dieser Liste muss an anderer Stelle abgefangen werden – in der Lockfile, im MCP-Client, an der Process Boundary um generierten Code oder durch Kontrollen, die verhindern, dass nicht vertrauenswürdige Instructions private Daten mit einem Exfiltration-Ziel verbinden.
Zentrale Erkenntnisse
- Content Filter und Execution Policy schützen unterschiedliche Boundaries. Filter prüfen Model Input und Output. Tool Authorization, Credential Scope, Sandboxes und Supply-Chain-Kontrollen wirken auf die Pfade, die in den sieben Incidents verwendet wurden.
- Für die meisten OWASP-ASI-Kategorien sind Kontrollen außerhalb des Model Outputs erforderlich. Nutze die Liste, um jede Bedrohung der Komponente zuzuordnen, die sie tatsächlich blockieren oder protokollieren kann.
- Berechtigungen sind Infrastruktur, kein Prompt. Claude Code dokumentiert die Precedence von Deny-, Ask- und Allow-Regeln, während
PreToolUsevor der Execution blockieren kann. Das Claude Agent SDK stellt einen separatencanUseTool-Pfad bereit. Andere Runtimes benötigen ein ebenso testbares Precedence Model. - Behandle ein strukturiertes Deny eines PreToolUse-Hooks wie jede andere Tool Observation. Der Reasoning Loop verarbeitet es bereits. Du brauchst keinen separaten Security Workflow.
- Eine Approval Rate von 93 % ist ein Signal, Prompt Quality und Escalation Frequency zu prüfen. Verfolge Edits, Denials und Incidents nach einem Approval, statt ein universelles Ziel zu kopieren.
- Audience-Bound Tokens und Per-Session-Vaults begrenzen Credential Replay und Exposure. Sie ersetzen weder Project Trust noch Hook Policy oder Sandboxing.
- Supply-Chain-Checks müssen mit Automation Speed laufen. Pinne Versionen und Action-SHAs, scanne in CI und prüfe Dependency Changes in Pull Requests, die von Agents erstellt wurden.
- Baue die Policy Layer so, dass ein neues Product Launch sie nicht entwertet. OpenAI Agents SDK, Codex CLI und Claude Code drücken dieselben Primitives unterschiedlich aus. Entscheidend sind die Primitives (Permission Ladders, Hooks, Sandboxes, Interrupts, Audience-Bound Tokens), auf die du setzt.
Die nächste Schicht ist die Runtime
Teil 5, Long-Running AI Agent Runtime, zeigt, wo Sandbox, Secret Broker, Checkpoint und Audit Trace während eines langen Runs liegen. Teil 6 geht anschließend ins Harness, wo diese Permission Ladder eine von mehreren Stages ist, und fragt, wie Acceptance Checks, Retries und Trace-Driven Evaluation verhindern, dass der Loop zu früh Erfolg meldet. Außerdem kommt eine Frage hinzu, die dieser Artikel nicht behandeln musste: ob ein Call, der mitten im Flug einen Timeout erreicht hat, überhaupt sicher erneut gesendet werden kann.
Referenzen
Die Framings
- Bharani Subramaniam und Martin Fowler, Emerging Patterns in Building GenAI Products.
- Simon Willison, The lethal trifecta for AI agents, 16. Juni 2025.
- Joel Fokou, Parallax: Why AI Agents That Think Must Never Act, arXiv 2604.12986, 14. April 2026 (nicht peer-reviewed).
- Alessandro Pignati, Your AI Agent Has Too Much Power: Understanding and Taming Excessive Agency, Januar 2026.
LLM-Guardrail-Produkte
- NVIDIA NeMo Guardrails
- Meta Llama Guard 4
- Guardrails AI
- Lakera Guard
- AWS Bedrock Guardrails
- Azure Content Safety: Prompt Shields
- openai-guardrails-python
Incidents
- Itay Ravia (ehemals Aim Labs, jetzt Cato Networks), Breaking down EchoLeak (CVE-2025-32711).
- AWS, Amazon Q Developer VS Code v1.84.0 advisory (CVE-2025-8217).
- Microsoft, Azure MCP Server CVE record (CVE-2026-32211; Vendor-Referenz: Microsoft).
- Check Point Research, RCE and API token exfiltration through Claude Code project files (CVE-2025-59536).
- axios, v1.14.1 / v0.30.4 compromise post-mortem.
- Aqua Security, Trivy Actions tag hijack (GHSA-69fq-xp46-6x23).
Policy Surfaces
- OpenAI Agents SDK – MCP Tools Docs
- Codex CLI Managed Configuration
- Claude Code Permission Modes
- Claude Code Sandboxing
- Claude Managed Agents
HITL
- LangGraph Interrupts Docs
- HumanLayer Python Quickstart
- Anthropic, Measuring AI agent autonomy in practice, 18. Februar 2026.
- Anthropic, Claude Code auto mode, 25. März 2026.
- Jackson Wells (Galileo), How to Build Human-in-the-Loop Oversight for Production AI Agents, 21. Dezember 2025.
OWASP
- OWASP Agentic Security Initiative, Top 10 for Agentic Applications, 2026, 9. Dezember 2025.
Die Policy Layer des Market Analyst Agents befindet sich im kombinierten Analysis-to-Trade-Graph des Repositories, nicht im in Teil 1 aufgeführten Analysis Graph. Sie steuert simulierten Trade State: Ein deterministischer Guardian Node lehnt eingeschränkte Actions ab, genehmigt Actions mit geringem Wert automatisch und eskaliert den Rest an einen Compliance-Officer-Node, bevor der Graph mit interrupt_before stoppt. Die Policy Layer liegt auf GitHub. Der Deny Hook, der Input Canary und der Stop-Hook-Validator oben sind Skizzen derselben Control Points. Sie sind zum Lesen gedacht, nicht zum direkten Einfügen in dieses Repository.