CLAUDE.mdに「.envファイルは絶対に編集しないこと」と書いても、AIは「おそらく」従う。Hooksに設定すれば、AIが.envに触れようとした瞬間に物理的に止まる。この「おそらく」と「必ず」の差が、本番環境の安全性を左右する。
CLAUDE.mdは「標識」、Hooksは「スピードバンプ」
CLAUDE.mdはClaude Codeが起動するたびに読み込まれるプロジェクト設定ファイルだ。コーディング規約、禁止事項、よく使うコマンド——ここに書けばAIに伝わる。ただし「伝わる」と「必ず守られる」は別の話だ。
交通ルールの看板を思い浮かべてほしい。「制限速度30km」という標識を立てても、見落としたドライバーは速度を落とさない。でも道路に物理的なスピードバンプがあれば、標識を読んでいなくても速度は落ちる。CLAUDE.mdは標識だ。Hooksはスピードバンプだ。
AIが便利になるほど、指示に従わないケースもゼロではなくなる。長いコンテキストで指示が埋もれることもあれば、AIが「この場合は例外」と判断して動くこともある。Hooksはその「例外」を許さない。AIが何を「考えて」いるかに関わらず、フックは実行される。
Hooksは「AIを信頼しない」ための仕組みではない。「大事なことは仕組みで保証する」という設計思想だ。git hooksで「テストが通らないとコミットできない」という制約を作るのと同じ論理——AIエージェント版のgit hooksだと思えば分かりやすい。
Hooksの仕組み——17箇所のイベントで介入できる
Claude CodeのHooksは、AIの処理ライフサイクルの17種類のイベントでシェルコマンド(またはHTTPリクエスト等)を実行できる仕組みだ。公式ドキュメントによれば、現在フックを設定できるイベントは以下の通りだ:
SessionStart、UserPromptSubmit、PreToolUse、PermissionRequest、PostToolUse、PostToolUseFailure、Notification、SubagentStart、SubagentStop、Stop、TeammateIdle、TaskCompleted、ConfigChange、WorktreeCreate、WorktreeRemove、PreCompact、SessionEnd
このうち実務で特に使うのが3つだ:
PreToolUse:ツール実行前に走る。ここでブロックするPostToolUse:ツール実行後に走る。ここで後処理を自動化するNotification:Claudeが入力待ちになったときに走る。通知に使う
フックの種別は4種類ある。command(シェルコマンド)、http(HTTPエンドポイント)、prompt(LLMに単発評価させる)、agent(マルチターンのサブエージェント)。日常的に使うのは command だ。
設定ファイルの場所
フックはJSONの設定ファイルに書く。置く場所によってスコープが変わる:
~/.claude/settings.json:自分の全プロジェクト共通。チームとは共有しない個人設定.claude/settings.json:プロジェクト固有。Gitにコミットしてチーム全体に適用可能.claude/settings.local.json:プロジェクト固有だが非共有(.gitignore済み)
セキュリティ系のフックはプロジェクトにコミットしてチーム全員に適用する。通知系のフックは個人設定に置く——この使い分けが実際には機能しやすい。
PreToolUse——実行前に止める
PreToolUseは、AIがツールを実行する直前に走るフックだ。ここがブロックの要になる。
公式ドキュメントによれば、PreToolUseフックが終了コード2(exit code 2)を返すと、そのツール実行がブロックされる。stderr(エラー出力)に書いたメッセージはClaudeへのフィードバックとして渡され、なぜブロックされたかをAIが理解できる。
また、permissionDecision フィールドを使って "allow" / "deny" / "ask" の3択で制御することもできる。updatedInput を使えばツールへの入力を書き換えることも可能だ。
.envファイルを保護する
AIが誤って環境変数ファイルを編集しようとするケースは珍しくない。.envに書いてあるAPIキーやデータベース認証情報がGitにコミットされれば、公開リポジトリなら即座に第三者に読まれる。Hooksで物理的に防ぐのが最も確実な対策だ。
公式ドキュメントの保護ファイル例をベースに実装する。フックはツール実行情報をJSON形式でstdinから受け取る。jq(JSONをシェルスクリプト内で扱うCLIツール)でそこから必要な値を取り出す仕組みだ:
#!/bin/bash
# .claude/hooks/protect-secrets.sh
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
PROTECTED_PATTERNS=(".env" ".env.local" ".env.production" "credentials" "secret")
for pattern in "${PROTECTED_PATTERNS[@]}"; do
if [[ "$FILE_PATH" == *"$pattern"* ]]; then
echo "Blocked: $FILE_PATH は保護されたファイルです。直接編集できません。" >&2
exit 2
fi
done
exit 0
次に実行権限を付与し、設定ファイルに登録する:
chmod +x .claude/hooks/protect-secrets.sh
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-secrets.sh"
}
]
}
]
}
}
matcher は正規表現が使える。Edit|Write はEditツールまたはWriteツールが実行されるときにマッチする。mcp__.* のようにMCPツール全体をまとめてマッチさせることもできる。
危険なBashコマンドをブロックする
Bashツールにもフックをかけられる。ルートディレクトリの削除、リモートスクリプトのパイプ実行、mainへのforce push——これらを物理的にブロックしておく:
#!/bin/bash
# .claude/hooks/guard-dangerous-commands.sh
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
# ルート削除をブロック
if echo "$COMMAND" | grep -qE 'rm\s+-[rf]+\s+/($|\s)'; then
echo "Blocked: ルートディレクトリの削除はできません" >&2
exit 2
fi
# リモートスクリプトのパイプ実行をブロック
if echo "$COMMAND" | grep -qE '(curl|wget).*\|\s*(sh|bash)'; then
echo "Blocked: リモートスクリプトのパイプ実行は禁止です" >&2
exit 2
fi
# main/masterへのforce pushをブロック
if echo "$COMMAND" | grep -qE 'git\s+push.*--force.*\s+(main|master)'; then
echo "Blocked: main/masterへのforce pushは禁止です" >&2
exit 2
fi
exit 0
公式ドキュメントでは「command hooksはフルのユーザー権限でシェルコマンドを実行する。あなたのユーザーアカウントがアクセスできるファイルを変更・削除・読み取れる」と警告している。フック自体がセキュリティリスクになりうるため、外部から取得したフックスクリプトは必ず中身を確認してから使うこと。
PostToolUse——実行後に自動化する
PostToolUseは、ツールが実行された後に走るフックだ。ツールはすでに実行済みのため、PostToolUseではブロックできない。代わりに「実行後に何か走らせる」自動化の場所として使う。
ファイル編集後に自動フォーマット
AIがファイルを編集するたびにPrettierを自動実行する例を公式ドキュメントが紹介している:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
}
]
}
]
}
}
フックにはStdin経由でツールの入出力情報がJSON形式で渡される。jq -r '.tool_input.file_path' で編集されたファイルのパスを取り出し、そのファイルにPrettierを実行する仕組みだ。
PreToolUseと違い、PostToolUseでexit code 2を返してもツール実行はすでに終わっているため取り消しはできない。「実行後に自動で後処理する」という用途に専念する。
Notification——待機中に通知を受け取る
AIが長いタスクをこなしている間、ずっと画面の前で待つ必要はない。Notificationイベントを使えば、Claudeが入力待ちになった瞬間にデスクトップ通知を飛ばせる。
公式ドキュメントによれば、Notificationイベントのmatcherには permission_prompt(権限確認が必要なとき)、idle_prompt(タスク完了で待機中)、auth_success(認証成功)、elicitation_dialog(追加情報が必要なとき)が使える。
タスクが完了してClaudeが待機状態になったときに通知する設定:
{
"hooks": {
"Notification": [
{
"matcher": "idle_prompt",
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude Codeが入力待ちです\" with title \"Claude Code\"'"
}
]
}
]
}
}
macOSの場合は osascript、Linuxの場合は notify-send 'Claude Code' 'Claude Codeが入力待ちです' で同様の通知ができる。
通知設定は個人の作業環境に依存するため、~/.claude/settings.json(グローバル設定)に置くのが適している。macOS専用のコマンドをプロジェクトの settings.json に書くと、Linux環境のチームメンバーのフックが失敗するからだ。
/hooksコマンドで始める
Hooksを設定する最も簡単な方法は、Claude Codeのセッション中に /hooks と入力することだ。インタラクティブなメニューが開き、イベントの選択からスクリプトの登録まで対話形式で設定できる。
もう一つの方法は .claude/settings.json を直接編集することだ。Gitで管理しやすく、チームへの共有も簡単なため、実務ではこちらが主流になる。
フックが正しく動いているか確認したいときは claude --debug で起動する。どのフックがマッチしたか、exit codeがいくつだったか、出力がどうだったかが詳細に表示される。
注意点が一つある。Hooksはセッション開始時のスナップショットで動作する。settings.json を直接編集しても、起動中のセッションには即時反映されない。/hooks メニューを経由した場合は反映されるので、動作確認は /hooks を使うのが確実だ。
3つのHooksで開発体験が変わる
実務ですぐ使える最小構成を示す。CLAUDE.mdとHooksの役割分担の本質はここにある:
<!-- ❌ CLAUDE.mdに書いても「お願い」止まり -->
## 禁止事項
- .envファイルを編集しないこと
- rm -rf は実行しないこと
// ✅ Hooksに書けば「強制」される
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [{ "type": "command", "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/protect-secrets.sh" }]
},
{
"matcher": "Bash",
"hooks": [{ "type": "command", "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/guard-dangerous-commands.sh" }]
}
],
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [{ "type": "command", "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write" }]
}
]
}
}
この設定で3つの自動化が走る:
- .envと認証情報ファイルへの書き込みがブロックされる
- 危険なBashコマンド(rm -rf /、curl | bash等)がブロックされる
- ファイル編集後にPrettierが自動実行される
タイムアウトのデフォルト値は、公式ドキュメントによれば commandが600秒(10分)、promptが30秒、agentが60秒だ。重い処理を走らせる場合は timeout フィールドで上書きできる。
HooksはAIエージェントの「物理法則」だ
CLAUDE.mdは「法律」に例えられることが多い。法律は守るべきルールだが、知らなければ違反するし、解釈の幅もある。Hooksはそれより一段下の「物理法則」だ。重力のように、AIが何を「考えていても」、フックは実行される。
.envを読もうとしたら止まる。ファイルを編集したらフォーマットが走る。タスクが終わったら通知が来る。
AIエージェントへの信頼が高まるほど、「信頼を前提にしない仕組み」も同時に育てる必要がある。それがHooksの役割だ。Claude Codeを使い込んでいくなら、CLAUDE.mdを書いた次のステップとしてHooksを設定することを勧める。