Die Claude Code settings.json entscheidet, was der Agent in Ihrem Projekt ohne Rückfrage darf, wo er nachfragen muss und was grundsätzlich ausgeschlossen ist. Ich zeige Ihnen, wie die Datei aufgebaut ist, welche Stolperstellen in der Regelsyntax stecken und wie ich die Konfiguration in einem eigenen Projekt gesetzt habe, in dem Claude Code gegen eine live WordPress-Website arbeitet.
Vier Dateien und eine feste Rangfolge
Claude Code liest Einstellungen aus mehreren JSON-Dateien. Welche Datei Sie wählen, bestimmt, für wen eine Einstellung gilt:
| Ebene | Datei | Gilt für |
|---|---|---|
| User | ~/.claude/settings.json |
Sie, in allen Projekten auf diesem Rechner |
| Projekt (geteilt) | .claude/settings.json |
alle, die im Projekt arbeiten, sobald die Datei eingecheckt ist |
| Projekt (lokal) | .claude/settings.local.json |
nur Sie, nur in diesem Projekt |
| Managed | managed-settings.json, MDM oder Admin-Konsole |
alle Geräte einer Organisation |
Wenn derselbe Schlüssel mehrfach gesetzt ist, gewinnt die höhere Ebene. Die Reihenfolge von oben nach unten lautet: Managed Settings, Kommandozeile (claude --settings), lokale Projektdatei, geteilte Projektdatei, User-Datei.
Für Listen gilt eine wichtige Ausnahme: permissions.allow und vergleichbare Arrays werden über alle Ebenen zusammengeführt statt ersetzt. Jede Datei kann also Regeln ergänzen, aber keine Regel einer anderen Datei entfernen. Das ist praktisch für Teams und zugleich der Grund, warum eine zu großzügige Regel in der User-Datei in jedem Projekt mitwirkt.
Eine Datei, die Claude Code selbst beschreibt, sollten Sie kennen: Wenn Sie bei einer Rückfrage „Yes, and don’t ask again“ wählen, landet die Freigabe als allow-Regel in .claude/settings.local.json. Wer sich über unerwartet viele Freigaben wundert, findet sie meist dort.
Die Projektdatei als Ausgangspunkt
So sieht eine gekürzte und verallgemeinerte Fassung der geteilten Projektdatei .claude/settings.json aus, die ich für die Pflege dieser Website verwende. Das Projekt enthält keinen eigenen Anwendungscode; Claude Code arbeitet dort vor allem über einen MCP-Server gegen eine WordPress-Installation. Entsprechend streng ist die Shell eingestellt.
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"sandbox": {
"enabled": true,
"failIfUnavailable": true,
"allowUnsandboxedCommands": false,
"filesystem": {
"denyRead": ["~/.ssh", "~/.aws", "./.env", "./**/.env"],
"denyWrite": ["~/.ssh", "~/.aws", "./.env", "./**/.env"]
},
"network": {
"allowedDomains": ["registry.npmjs.org", "github.com"]
}
},
"permissions": {
"allow": ["Bash(git status *)", "Bash(git diff *)", "Bash(git log *)"],
"ask": ["Bash(git commit *)", "Bash(npx *)"],
"deny": [
"Read(./.env)",
"Read(./**/.env)",
"Read(~/.ssh/**)",
"Bash(git push *)",
"Bash(git reset *)",
"Bash(sudo *)"
]
}
}
Kommentare gehören nicht in diese Datei: Die Dokumentation beschreibt die Settings-Dateien als striktes JSON, ein //-Kommentar oder ein Komma am Ende ist ein Syntaxfehler. Die $schema-Zeile verweist auf das veröffentlichte JSON-Schema und bringt Autovervollständigung und Validierung in Editoren wie VS Code. Das Schema kann neueren CLI-Versionen hinterherhinken, eine Warnung bei einem frisch dokumentierten Schlüssel ist also nicht automatisch ein Fehler.
Permissions in der Claude Code settings.json: allow, ask, deny
Jede Regel hat die Form Tool oder Tool(specifier). Bash allein trifft jeden Shell-Befehl, Bash(npm run build) genau diesen einen Befehl, WebFetch(domain:example.com) Abrufe dieser Domain.
Die Auswertungsreihenfolge
Claude Code prüft Regeln in fester Reihenfolge: erst deny, dann ask, dann allow. Der erste Treffer entscheidet, die Spezifität einer Regel spielt keine Rolle. Eine breite Sperre wie Bash(aws *) lässt sich deshalb nicht durch eine engere Freigabe wie Bash(aws s3 ls) aufweichen. In meiner Konfiguration heißt das: git commit fragt immer nach, auch wenn später jemand eine allgemeinere Git-Freigabe ergänzt.
Ein Detail für Teams: allow-Regeln aus der eingecheckten Projektdatei greifen erst, nachdem die jeweilige Person dem Ordner vertraut hat. deny– und ask-Regeln gelten sofort.
Wildcards in Bash-Regeln
Ein * steht für beliebigen Text, auch für Leerzeichen. Alles vor dem ersten * wird wörtlich verglichen. Daraus ergeben sich zwei Regeln, die ich anfangs falsch gemacht habe:
- Das Leerzeichen vor dem
*zählt.Bash(ls *)trifftls -la, aber nichtlsof.Bash(ls*)trifft beides. Meine ursprüngliche Datei enthieltBash(git status*)ohne Leerzeichen; funktional harmlos, aber unpräziser als nötig. - Den
*hinter das Unterkommando setzen.Bash(git log *)erlaubt nurgit log,Bash(git *)erlaubt jeden Git-Befehl, auchgit push.
Verkettete Befehle mit &&, ||, ; oder | zerlegt Claude Code in Teilbefehle, und eine Freigabe muss jeden Teil einzeln abdecken. deny– und ask-Regeln greifen, sobald irgendein Teil passt.
Warum eine deny-Regel keine Sicherheitsgrenze ist
Bash-Regeln vergleichen den Befehlstext. Bash(git push *) stoppt git push origin main, aber nicht git -C . push origin main. Bash(rm *) stoppt nicht /bin/rm oder bash -c 'rm ...'. Die Dokumentation sagt das ausdrücklich und verweist für eine belastbare Grenze auf die Sandbox oder auf PreToolUse-Hooks.
Ich nutze deny-Regeln deshalb als Leitplanke für die üblichen Formulierungen und verlasse mich für echte Isolation auf die Sandbox.
Pfade in Read- und Edit-Regeln
Read– und Edit-Regeln verwenden gitignore-Syntax, mit vier Präfixen:
| Muster | Bedeutung |
|---|---|
//pfad |
absoluter Pfad ab Dateisystem-Wurzel |
~/pfad |
relativ zum Home-Verzeichnis |
/pfad |
relativ zur Settings-Quelle (in Projektdateien: Arbeitsverzeichnis) |
pfad oder ./pfad |
relativ zum aktuellen Verzeichnis |
Der häufigste Fehler: Read(/Users/name/geheim) ist kein absoluter Pfad. Der einfache Schrägstrich verankert an der Settings-Quelle; absolut wird es erst mit //. In der User-Datei zeigt /secrets/** sogar auf ~/.claude/secrets/**.
Eine Read-Sperre gilt für die eingebauten Datei-Tools und für Shell-Befehle, die Claude Code als Dateizugriff erkennt, etwa cat, head oder tail. Sie greift nicht bei Befehlen, die Dateien lesen, ohne sie zu benennen, zum Beispiel grep -r muster .. Auch das spricht dafür, sensible Pfade zusätzlich in der Sandbox zu sperren.
Standardmodus festlegen
Mit permissions.defaultMode legen Sie fest, in welchem Modus eine Sitzung startet, etwa default, acceptEdits oder plan. Die Modi auto und bypassPermissions wirken nicht aus Projektdateien, sondern nur aus User- oder Managed Settings. Ein eingechecktes Repository kann Ihnen also nicht unbemerkt alle Rückfragen abschalten.
Die Sandbox: Grenzen auf Betriebssystemebene
Die Sandbox arbeitet anders als Permissions. Sie prüft keinen Befehlstext, sondern das Betriebssystem setzt Datei- und Netzwerkgrenzen für jeden Bash-, PowerShell- und Monitor-Befehl samt Kindprozessen durch. Auf macOS nutzt Claude Code dafür Seatbelt, auf Linux und WSL2 bubblewrap.
Was standardmäßig erlaubt ist
- Schreiben: Arbeitsverzeichnis, zusätzlich freigegebene Verzeichnisse und ein temporäres Verzeichnis pro Nutzer.
- Lesen: der gesamte Rechner, abgesehen von einigen gesperrten Bereichen. Das schließt Dateien wie
~/.aws/credentialsund~/.ssh/ausdrücklich ein. - Netzwerk: keine Domain ist vorab freigegeben. Beim ersten Zugriff fragt Claude Code nach; mit
sandbox.network.allowedDomainssparen Sie sich die Rückfrage. Domains ausWebFetch(domain:...)-Freigaben fließen ebenfalls in die Liste ein.
Der Lese-Standard ist der Grund, warum ich ~/.ssh und ~/.aws in sandbox.filesystem.denyRead eintrage. Alternativ bietet sandbox.credentials einen eigenen Block für Zugangsdaten-Dateien und Umgebungsvariablen.
Auto-Allow und die Hintertür schließen
Im Auto-Allow-Modus laufen Befehle, die in die Sandbox passen, ohne Rückfrage; explizite deny-Regeln und inhaltliche ask-Regeln wie Bash(git push *) gelten trotzdem. Den Modus wählen Sie über /sandbox.
Scheitert ein Befehl an der Sandbox, darf Claude ihn standardmäßig außerhalb erneut versuchen, dann mit normaler Rückfrage. "allowUnsandboxedCommands": false schaltet diese Möglichkeit ab. Und falls die Sandbox gar nicht starten kann, etwa wegen fehlender Pakete unter Linux, läuft Claude Code standardmäßig mit einer Warnung ungeschützt weiter. "failIfUnavailable": true macht daraus einen harten Fehler. In meinem Projekt sind beide Schalter gesetzt, weil dort Schreibzugriffe eine Produktivseite treffen.
Was die Sandbox nicht leistet
Die Sandbox betrifft nur Shell-Befehle. Die Datei-Tools Read, Edit und Write laufen über das Permission-System. Außerdem prüft der eingebaute Proxy Domains anhand des Hostnamens, ohne TLS-Verkehr zu inspizieren. Breite Freigaben wie github.com können deshalb Wege für Datenabfluss öffnen. github.com steht auch in meiner Liste; ich halte die Liste deshalb kurz und prüfe sie, wenn sich der Projektzweck ändert.
Einige Dateien sind innerhalb beschreibbarer Verzeichnisse trotzdem geschützt, darunter die .claude-Settings-Dateien und die Ordner .claude/skills, .claude/agents und .claude/hooks. Ein allowWrite-Eintrag hebt diesen Schutz nicht auf. Ein Befehl kann sich also nicht selbst neue Rechte eintragen.
Prüfen, was tatsächlich gilt
Nach jeder Änderung kontrolliere ich drei Stellen:
# Slash-Befehle innerhalb einer Claude-Code-Sitzung
/status # Zeile "Setting sources": welche Dateien geladen wurden
/permissions # alle Regeln mit der Datei, aus der sie stammen
/sandbox # Reiter "Config": aufgelöste Sandbox-Einstellungen
/status zeigt, welche Dateien geladen wurden, aber nicht, welche Datei welchen Schlüssel geliefert hat. Abgelehnte Einträge listet claude doctor. Änderungen an permissions übernimmt Claude Code meist ohne Neustart.
Fragen oder Anmerkungen zu diesem Beitrag?
