Claude Code Hooks: Sichere Checks vor und nach Agentenarbeit
Praxisnaher Claude Code Hooks Leitfaden für Einsteiger: riskante Befehle blockieren, Logs speichern, formatieren und testen.
Claude Code Hooks sind automatische Prüfungen, die vor oder nach einer Aktion von Claude Code laufen. Statt in jedem Prompt wieder zu schreiben “führe keine gefährlichen Befehle aus”, “formatiere die Datei” oder “starte die Tests”, kannst du diese Regeln in den Workflow des Projekts einbauen.
Das einfachste mentale Modell lautet: PreToolUse ist die Bremse, PostToolUse ist die Wartung, UserPromptSubmit ist das Eingangsprotokoll, und Stop ist die Kontrolle vor dem Verlassen. Hooks ersetzen keine menschliche Review, keine Berechtigungen und keine CI. Sie sorgen dafür, dass wiederkehrende Prüfungen nicht vergessen werden.
Dieser Artikel folgt der offiziellen Claude Code Hooks Referenz und der offiziellen Settings-Dokumentation. Für Projektkontext lies zusätzlich CLAUDE.md Best Practices. Für Sicherheitsregeln passt der Claude Code Permissions Guide dazu.
Vier Events, die du zuerst kennen solltest
Claude Code unterstützt mehrere Hook-Events, aber für den praktischen Einstieg reichen meistens diese vier.
| Event | Wofür es passt | Beispiel |
|---|---|---|
UserPromptSubmit | Bevor der Prompt an Claude geht | Anfrage speichern, Secrets erkennen, leichten Kontext ergänzen |
PreToolUse | Direkt vor Tool-Ausführung | Destruktive Bash-Befehle oder Produktionsaktionen blockieren |
PostToolUse | Nach erfolgreicher Tool-Ausführung | Formatter, Lint oder passende Tests ausführen |
Stop | Wenn Claude die Antwort beenden will | Konflikte prüfen, Zusammenfassung speichern, fehlende Prüfung melden |
Nutze PreToolUse, wenn etwas nicht passieren darf. Nutze PostToolUse, wenn etwas passiert ist und aufgeräumt oder geprüft werden soll. Nutze UserPromptSubmit, wenn du die Qualität der Anfragen verstehen möchtest. Nutze Stop, wenn eine Session nicht mit einem offensichtlichen Problem enden soll.
Gemeinsame Regeln liegen meist in .claude/settings.json. Persönliche Experimente gehören eher in .claude/settings.local.json, damit sie nicht versehentlich ins Repository kommen. Im Team sollte dokumentiert sein, welche Hooks blockieren, welche nur protokollieren und wer sie pflegt.
Minimale Konfiguration zum Kopieren
Diese Konfiguration speichert Prompts, blockiert gefährliche Bash-Befehle, startet Qualitätschecks nach Dateiänderungen und speichert am Ende eine kurze Zusammenfassung.
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "node .claude/hooks/log-prompt.mjs"
}
]
}
],
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "node .claude/hooks/block-dangerous-command.mjs"
}
]
}
],
"PostToolUse": [
{
"matcher": "Edit|Write|MultiEdit",
"hooks": [
{
"type": "command",
"command": "node .claude/hooks/run-quality-checks.mjs",
"timeout": 120
}
]
}
],
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "node .claude/hooks/stop-summary.mjs"
}
]
}
]
}
}
Wichtig ist das verschachtelte hooks Array. Die aktuelle Struktur definiert zuerst das Event, optional einen matcher, und dann Handler wie type: "command". Ältere Beispiele mit command direkt neben matcher tauchen noch auf, sind aber nicht das Muster, das man heute kopieren sollte.
Use case 1: Gefährliche Befehle mit PreToolUse blockieren
Der erste use case ist Sicherheit bei Bash. Wenn Claude Code schnell arbeitet, ist der riskante Moment nicht nach dem Befehl, sondern direkt davor. PreToolUse kann die Bash-Eingabe lesen und eine offensichtlich gefährliche Aktion verweigern.
Speichere die Datei als .claude/hooks/block-dangerous-command.mjs.
import fs from "node:fs";
const input = JSON.parse(fs.readFileSync(0, "utf8") || "{}");
const command = String(input.tool_input?.command || "");
const denyPatterns = [
/rm\s+-rf\s+(\/|\*|\.|\$HOME)/i,
/cat\s+\.env(\.|$|\s)/i,
/printenv/i,
/aws\s+.*\s+delete-/i,
/gcloud\s+.*\s+delete/i,
/kubectl\s+delete\s+(namespace|deployment|secret)/i,
/DROP\s+DATABASE/i
];
const matched = denyPatterns.find((pattern) => pattern.test(command));
if (matched) {
console.log(JSON.stringify({
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: `Blocked by project hook: ${matched}`
}
}));
process.exit(0);
}
Das ist kein vollständiges Sicherheitsprodukt. Reguläre Ausdrücke können umgangen werden. Trotzdem verhindert es die häufigsten groben Fehler: .env anzeigen, breite Verzeichnisse löschen, Cloud-Ressourcen entfernen, Kubernetes-Objekte löschen oder destruktives SQL starten. Für Produktion brauchst du zusätzlich Claude Code Permissions, geschützte Branches, CI und menschliche Freigabe.
Use case 2: Prompts mit UserPromptSubmit protokollieren
Der zweite use case ist Prompt-Observability. Viele schlechte Sessions entstehen nicht durch ein schwaches Modell, sondern durch eine unklare Anfrage: “mach es besser”, “räum auf”, “fix das”. Mit einem Prompt-Log kannst du später sehen, welche Formulierungen gute Ergebnisse erzeugen.
Speichere die Datei als .claude/hooks/log-prompt.mjs.
import fs from "node:fs";
import path from "node:path";
const input = JSON.parse(fs.readFileSync(0, "utf8") || "{}");
const dir = path.join(process.cwd(), ".claude", "hook-logs");
fs.mkdirSync(dir, { recursive: true });
const record = {
time: new Date().toISOString(),
event: input.hook_event_name,
cwd: input.cwd,
prompt: input.prompt || input.user_prompt || ""
};
fs.appendFileSync(
path.join(dir, "prompts.jsonl"),
JSON.stringify(record) + "\n",
"utf8"
);
Dieses Log sollte zuerst lokal bleiben. Prompts können Kundennamen, interne URLs, versehentlich kopierte Tokens oder vertraulichen Geschäftskontext enthalten. Füge .claude/hook-logs/ zu .gitignore hinzu und definiere Aufbewahrung, Zugriff und Verantwortung.
Use case 3: Format und Tests nach Änderungen ausführen
Der dritte use case ist die tägliche Qualitätsroutine. Nach einer Dateiänderung startet der Hook einen Formatter und eine kleine Prüfung. Dadurch musst du nicht in jedem Prompt “bitte formatieren und testen” ergänzen.
Speichere die Datei als .claude/hooks/run-quality-checks.mjs.
import fs from "node:fs";
import { execFileSync } from "node:child_process";
const input = JSON.parse(fs.readFileSync(0, "utf8") || "{}");
const filePath = String(input.tool_input?.file_path || "");
const isSourceFile = /\.(js|jsx|ts|tsx|css|md|mdx|json)$/.test(filePath);
if (!isSourceFile) process.exit(0);
const run = (cmd, args) => {
try {
return execFileSync(cmd, args, {
cwd: process.cwd(),
encoding: "utf8",
stdio: ["ignore", "pipe", "pipe"]
});
} catch (error) {
return String(error.stdout || "") + String(error.stderr || "");
}
};
const messages = [];
messages.push(run("npx", ["prettier", "--write", filePath]));
if (/\.(js|jsx|ts|tsx)$/.test(filePath)) {
messages.push(run("npm", ["test", "--", "--runInBand"]));
}
console.log(JSON.stringify({
hookSpecificOutput: {
hookEventName: "PostToolUse",
additionalContext: messages.join("\n").slice(-4000)
}
}));
In einem großen Monorepo solltest du nicht bei jeder Änderung die komplette Testsuite laufen lassen. Starte mit Formatierung, ergänze später dateinahe Tests und verschiebe lange Prüfungen in async Hooks oder CI. Kürze außerdem die Ausgabe, die an Claude zurückgeht, damit der Kontext nicht mit Logs gefüllt wird.
Use case 4: Die Session mit Stop prüfen
Stop läuft, wenn Claude die Antwort beenden möchte. Es eignet sich für eine Zusammenfassung oder für enge Blockaden, zum Beispiel ungelöste Git-Konflikte.
import fs from "node:fs";
import { execFileSync } from "node:child_process";
const input = JSON.parse(fs.readFileSync(0, "utf8") || "{}");
if (input.stop_hook_active) {
process.exit(0);
}
let status = "";
try {
status = execFileSync("git", ["status", "--short"], {
cwd: process.cwd(),
encoding: "utf8"
});
} catch {
process.exit(0);
}
if (status.includes("UU ")) {
console.error("Git conflict remains. Resolve conflicts before finishing.");
process.exit(2);
}
fs.mkdirSync(".claude/hook-logs", { recursive: true });
fs.appendFileSync(
".claude/hook-logs/stop.jsonl",
JSON.stringify({
time: new Date().toISOString(),
lastAssistantMessage: input.last_assistant_message || "",
gitStatus: status.slice(0, 2000)
}) + "\n"
);
Sei mit Stop vorsichtig. Wenn du zu viel blockierst, wirkt der Agent, als käme er nicht mehr aus der Session heraus. Prüfe stop_hook_active, halte Bedingungen eng und beginne eher mit Logging als mit Blockieren.
Pitfall: Häufige Fehler und Gegenmaßnahmen
Der erste pitfall ist, Hooks als Ersatz für Berechtigungen zu behandeln. Ein command hook läuft mit den Rechten deines Systembenutzers. Ein schlechter Hook kann Secrets lesen oder Dateien löschen. Validiere Eingaben, prüfe Pfade, meide .env und .git, und quote Pfade sauber.
Der zweite pitfall ist zu viel synchrone Arbeit. Eine Datei zu formatieren ist okay. Nach jeder Änderung Build, komplette Tests, Browser-Tests und Deploy zu starten, ist meist zu schwer. Schnelle Checks bleiben in PostToolUse; schwere Checks gehören in async Hooks oder CI.
Der dritte pitfall ist Logging ohne Richtlinie. Logs helfen beim Debugging, können aber vertrauliche Informationen enthalten. Definiere Speicherort, Aufbewahrung, Zugriff und Git-Ausschluss.
Der vierte pitfall ist die Verwechslung von matcher und Logik. matcher: "Bash" filtert nur Bash-Events zum Handler. Die eigentliche Entscheidung muss das vollständige Kommando im Skript prüfen.
Masa-artige Verifikationsnotiz
Im Test brachte die Rückgabe von Format- und Testergebnissen über additionalContext den größten Nutzen. Claude konnte den nächsten Fehler selbst sehen, ohne dass jemand Logs in den Chat kopiert. Das Blockieren gefährlicher Befehle ist ebenfalls wertvoll, nicht weil es perfekt ist, sondern weil es offensichtliche Fehler genau im richtigen Moment stoppt.
Für ein neues Team würde ich es in drei Schritten ausrollen: eine Woche Prompt-Logs, danach Formatierung nach Dateiänderungen, danach eine kleine Liste verbotener Bash-Muster. Dieser Ablauf erzeugt Vertrauen, bevor Reibung entsteht.
Wenn du das im Team einführen willst, bündelt die Claude Code Training Seite Permissions, CLAUDE.md, Hooks und Review Workflow. Für Solo-Entwickler kann derselbe Ablauf als Gumroad-Checkliste für neue Projekte dienen.
Zusammenfassung
Claude Code Hooks sind praktische Guardrails. Nutze UserPromptSubmit, um Anfragen zu verstehen, PreToolUse, um riskante Aktionen zu blockieren, PostToolUse, um Änderungen zu prüfen, und Stop, um nicht mit offensichtlichen Restproblemen zu enden.
Starte klein. Ein einfacher Hook, den das Team aktiviert lässt, ist wertvoller als eine große Automatisierung, die nach zwei Tagen ausgeschaltet wird.
Kostenloses PDF: Claude-Code-Cheatsheet
E-Mail eintragen und eine Seite mit Befehlen, Review-Gewohnheiten und sicheren Workflows herunterladen.
Wir schützen Ihre Daten und senden keinen Spam.
Über den Autor
Masa
Engineer für praktische Claude-Code-Workflows und Team-Einführung.
Ähnliche Artikel
Claude-Code-Permission-Receipt: Scope, Beweis und Rollback festhalten
Permission-Receipt für Claude Code: erlaubte Aktionen, Freigabegrenzen, Prüfbefehle, Rollback und Umsatz-CTA-Prüfung.
Sicheres Agent Harness fur Claude Code und Codex: Rechte, Prufung und Rollback
Ein praktisches Agent Harness fur Claude Code und Codex mit Policy, Plan, Verifikation und Recovery.
Claude Code Subagents: Praxisleitfaden für sichere Agent-Delegation
Claude Code Subagents praktisch nutzen: Artikel- und Codearbeit sicher aufteilen, Prompts einsetzen, Fehler vermeiden.