Claude Code: Hooks als Sicherheitsnetz gegen falsche Befehle
Claude Code führt Befehle zuverlässig aus, was problematisch wird, wenn es der falsche ist. Mit Hooks lässt sich vor jeder Aktion eine Sicherheitsprüfung einbauen, die nicht darauf angewiesen ist, dass das Modell Regeln beachtet. Konfiguriert werden sie über die Datei .claude/settings.json.

Claude Code ist sehr gut darin, Befehle auszuführen. Genau das ist das Problem, wenn es den falschen ausführt. Hooks erlauben es dir, ein Sicherheitsnetz einzuziehen, das vor jeder Aktion läuft, ohne dass sich das Modell an deine Regeln "erinnern" muss.
.claude/hooks/block-dangerous-commands.sh:
#!/usr/bin/env bash input = " $( cat ) " patterns =( 'git[^;&|]*[[:space:]]push([[:space:]][^;&|]*)?[[:space:]](--force([^-]|$)|-[a-zA-Z]*f[a-zA-Z]*([[:space:]"]|$)|\+[^[:space:]"]+)' 'git[^;&|]*[[:space:]]reset([[:space:]][^;&|]*)?[[:space:]]--hard' 'DROP[[:space:]]+(TABLE|DATABASE)' '(curl|wget)[^|]*\|[[:space:]]*(sudo[[:space:]]+)?(ba)?sh' ) for pattern in " ${ patterns [@] } "; do if printf '%s' " $input " | grep -qiE -- " $pattern "; then echo "Blocked: matches a dangerous pattern. Ask the user to run it." > &2 exit 2 fi done exit 0 Vollbildmodus aktivieren Vollbildmodus beenden Zwei Details im Push-Pattern: --force([^-]|$) blockiert --force, lässt aber --force-with-lease durch, was die sichere Variante des Force-Push ist; und \+[^[:space:]"]+ erfasst eine + Refspec ( git push origin +main ), die einen Force-Push ermöglicht, ohne dass --force jemals eingegeben wird. Das Präfix git[^;&|]* erfasst außerdem auch git -C some/dir push --force und git reset -q --hard.
{ "hooks": { "PreToolUse": [ { "matcher": "Bash", "hooks": [ { "type": "command", "command": " \" $CLAUDE_PROJECT_DIR \" /.claude/hooks/block-dangerous-commands.sh" } ] } ] } } Vollbildmodus aktivieren Vollbildmodus beenden Vergiss chmod +x nicht und teste es anschließend:
echo '{"tool_input":{"command":"git push origin +main"}}' |.claude/hooks/block-dangerous-commands.sh; echo $? # → 2 Vollbildmodus aktivieren Vollbildmodus beenden Ich habe es in einer echten Sitzung getestet: Ich bat Claude, git push --force origin main auszuführen, und es kam mit der Antwort zurück, der Befehl sei "von einem PreToolUse-Hook blockiert" worden, ich solle ihn selbst ausführen, wenn ich ihn wirklich brauche.
Defense in depth: Ein Regex-Hook ist eine Frühwarnung, keine Garantie. Ein Leser der spanischen Version dieses Beitrags fand zwei Varianten des Force-Push, die an meinen ersten Patterns vorbeischlüpften (beide oben korrigiert). Bei allem, was irreversibel ist, lege die echte Sperre dorthin, wo der Agent sie nicht ändern kann: Branch Protection auf dem Remote mit deaktivierten Force-Pushes. Wenn der Regex versagt, prallt der Push am Server ab.
Dasselbe Muster mit matcher: "Write|Edit|MultiEdit" und Regexes wie AKIA[0-9A-Z]{16} (AWS), sk-ant-[A-Za-z0-9_-]{20,} (Anthropic) oder gh[pousr]_[A-Za-z0-9]{36,} (GitHub). Wenn Claude versucht, einen Key zu schreiben, erhält es die Rückmeldung, es solle "eine Umgebungsvariable verwenden und diese in.env.example dokumentieren", und behebt es daraufhin von selbst.
Ein PostToolUse-Hook, der file_path aus dem JSON liest und das Prettier/Biome des Projekts ausführt ( node_modules/.bin/prettier ). Er beendet sich immer mit 0: Er blockiert nie, er räumt nur auf.
Die Hooks 1 und 2, mit 27 Tests unter Linux und macOS, stehen auf GitHub unter MIT-Lizenz: https://github.com/kailucho/claude-code-hooks-seguridad (Dokumentation auf Spanisch, englische Zusammenfassung am Anfang).
Das Starter Kit für Claude Code (9 US-Dollar, inoffiziell) bündelt die drei Hooks plus einen Benachrichtigungs-Hook, 6 Skills ( /commit, /pr, /review, /tests, /plan, /debug ), CLAUDE.md-Vorlagen für React, Node und GraphQL sowie Berechtigungs-Presets: https://luijhy.gumroad.com/l/starter-kit-for-claude-code
Welchen Befehl würdest du noch blockieren? Lass es mich in den Kommentaren wissen.
Automatisch übersetzt. Das Original auf Englisch ist unten aufklappbar.
Original (Englisch) anzeigen
Claude Code is very good at running commands. That's exactly the problem when it runs the wrong one. Hooks let you add a safety net that runs before every action, without relying on the model "remembering" your rules.
.claude/hooks/block-dangerous-commands.sh:
#!/usr/bin/env bash input = " $( cat ) " patterns =( 'git[^;&|]*[[:space:]]push([[:space:]][^;&|]*)?[[:space:]](--force([^-]|$)|-[a-zA-Z]*f[a-zA-Z]*([[:space:]"]|$)|\+[^[:space:]"]+)' 'git[^;&|]*[[:space:]]reset([[:space:]][^;&|]*)?[[:space:]]--hard' 'DROP[[:space:]]+(TABLE|DATABASE)' '(curl|wget)[^|]*\|[[:space:]]*(sudo[[:space:]]+)?(ba)?sh' ) for pattern in " ${ patterns [@] } "; do if printf '%s' " $input " | grep -qiE -- " $pattern "; then echo "Blocked: matches a dangerous pattern. Ask the user to run it." > &2 exit 2 fi done exit 0 Enter fullscreen mode Exit fullscreen mode Two details in the push pattern: --force([^-]|$) blocks --force but lets --force-with-lease through, which is the safe way to force-push; and \+[^[:space:]"]+ catches a + refspec ( git push origin +main ), which forces without ever typing --force. The git[^;&|]* prefix also catches git -C some/dir push --force and git reset -q --hard.
{ "hooks": { "PreToolUse": [ { "matcher": "Bash", "hooks": [ { "type": "command", "command": " \" $CLAUDE_PROJECT_DIR \" /.claude/hooks/block-dangerous-commands.sh" } ] } ] } } Enter fullscreen mode Exit fullscreen mode Don't forget chmod +x, then test it:
echo '{"tool_input":{"command":"git push origin +main"}}' |.claude/hooks/block-dangerous-commands.sh; echo $? # → 2 Enter fullscreen mode Exit fullscreen mode I tested it in a real session: I asked Claude to run git push --force origin main and it came back with "blocked by a PreToolUse hook, run it yourself if you really need it".
Defense in depth: a regex hook is an early warning, not a guarantee. A reader of the Spanish version of this post found two forced-push variants that slipped past my first patterns (both fixed above). For anything irreversible, put the real block where the agent can't edit it: branch protection on the remote with force pushes disabled. If the regex falls short, the push bounces off the server.
Same pattern with matcher: "Write|Edit|MultiEdit" and regexes like AKIA[0-9A-Z]{16} (AWS), sk-ant-[A-Za-z0-9_-]{20,} (Anthropic) or gh[pousr]_[A-Za-z0-9]{36,} (GitHub). When Claude tries to write a key, it gets "use an environment variable and document it in.env.example " and fixes it on its own.
A PostToolUse hook that reads file_path from the JSON and runs the project's own Prettier/Biome ( node_modules/.bin/prettier ). It always exits 0: it never blocks, it just tidies.
Hooks 1 and 2, with 27 tests running on Linux and macOS, are on GitHub under MIT: https://github.com/kailucho/claude-code-hooks-seguridad (docs in Spanish, English summary at the top).
The Starter Kit for Claude Code (US$9, unofficial) bundles the three hooks plus a notifications one, 6 skills ( /commit, /pr, /review, /tests, /plan, /debug ), CLAUDE.md templates for React, Node and GraphQL, and permission presets: https://luijhy.gumroad.com/l/starter-kit-for-claude-code
What other command would you block? Let me know in the comments.