Claude Code Hooks で AI の暴走を止める|.env 読み込み拒否から自動 lint まで
📝 はじめに
Claude Code は強力です。ファイルを読み書きし、コマンドを実行し、Git 操作まで自動でこなしてくれます。
でも、強力であるがゆえの怖さ を感じたことはありませんか?
🔹 こんな事故、起きていませんか?
.envをうっかり読まれた — API キーが会話ログに載ってしまったrm -rf distのつもりがrm -rf .だった — プロジェクトごと消えたgit push --forceされた — チームメンバーのコミットが吹き飛んだ- 本番用の設定ファイルを書き換えられた — デプロイしたらサイトが落ちた
「いやいや、Claude Code には許可プロンプトがあるから大丈夫でしょ?」と思うかもしれません。確かに Claude Code はファイルの書き込みやコマンド実行の前に「これを実行してもいいですか?」と聞いてきます。
でも、実際の作業風景を思い出してください。Claude Code と長時間作業していると、許可プロンプトが 何十回、何百回 と表示されます。集中しているときに「Y → Enter」を連打していませんか? 私はしていました。
人間の注意力に依存するセキュリティは、いつか必ず破綻します。
ヒューマンエラーは防ぐものではなく、仕組みで発生しないようにするもの です。交通事故を減らすのに「注意しましょう」では不十分で、ガードレールや信号機が必要なのと同じこと。
そこで登場するのが Hooks(フック) です。
Hooks を使えば、ツール実行の前後にシェルスクリプトを自動で挟み込み、危険な操作をプログラム的にブロックできます。Y を連打しようが、寝ぼけていようが、スクリプトが exit 1 を返せば 操作は絶対に実行されません 。
この機能、英語圏では Reddit や X で設定例が出回り始めていますが、日本語の解説はほぼ皆無 です。この記事で基礎の考え方から実践レシピまで、がっつり紹介します。
🚨 危険
本記事に掲載しているスクリプトには AI によって生成されたコードが含まれています。あくまで考え方と導入の参考例であり、そのまま使えば完璧なセキュリティ対策になるわけではありません 。ご自身の環境に合わせて検証・カスタマイズしたうえでご利用ください。
Advertisement
📝 Hooks とは?
🔹 一言でいうと
Hooks は Claude Code の イベント駆動型フィルター機能 です。
「Claude がファイルを読もうとした」「コマンドを実行しようとした」「ファイルを書き込もうとした」——こうした イベントが発生するたびに、あなたが書いたシェルスクリプトが自動で走る 仕組みです。
🔹 たとえ話で理解する
Hooks は 空港のセキュリティゲート のようなものです。
- 旅客(Claude)が搭乗口(ファイル操作)に向かう
- ゲート(Hook)が自動でスキャン(スクリプトを実行)
- 問題なければ通過(
exit 0) - 危険物が検出されたらブロック(
exit 1)
旅客がどれだけ急いでいても、ゲートは例外なくチェックします。これが「人間の判断に頼らない安全装置」の本質です。
🔹 設定ファイルの場所
Hooks は .claude/settings.json または .claude/settings.local.json に書きます。
{
"hooks": {
"PreToolUse": [
{
"matcher": "Read",
"command": "bash .claude/hooks/block-env.sh"
}
]
}
}
このように、「いつ」「何のツールで」「何を実行するか」 を宣言的に書けます。一度書いたら、あとは Claude Code が自動で毎回チェックしてくれます。
🔹 なぜ CLAUDE.md だけでは不十分なのか
「.env を読まないで」と CLAUDE.md に書いておけばいいじゃないか、と思うかもしれません。
実際、CLAUDE.md に書いたルールは Claude もかなり守ってくれます。でも 100% ではない 。AI は確率的なモデルであり、長い会話の中でルールを「忘れる」ことがあります。特にコンテキストが長くなったとき、CLAUDE.md の指示が薄れることがあるのです。
| 手段 | 強制力 | 仕組み |
|---|---|---|
| CLAUDE.md | お願いベース | AI が読んで判断(忘れることもある) |
| Hooks | 強制ベース | プログラムが毎回チェック(例外なし) |
「CLAUDE.md でガイドし、Hooks でガードする」 という二層構造が理想です。CLAUDE.md は「こうしてほしい」という方針、Hooks は「これだけは絶対にやらせない」というハードリミット。この組み合わせが最強です。
Advertisement
📝 3 種類のフック
Hooks には 3 つのタイミングがあります。
| フック | タイミング | 主な用途 |
|---|---|---|
| PreToolUse | ツール実行の 前 | 危険な操作のブロック・確認 |
| PostToolUse | ツール実行の 後 | 自動 lint・フォーマット・通知 |
| UserPromptSubmit | ユーザーが入力を送信した時 | 入力の前処理・ログ記録 |
🔹 PreToolUse — 実行前にブロックできる
最も重要なフック です。Claude がツール(Read, Write, Edit, Bash など)を使おうとした瞬間に発火します。
なぜこれが一番大事か?
他の 2 つのフックは「事後処理」や「入力処理」ですが、PreToolUse だけは 「実行させない」ことができる からです。.env を読ませない、rm -rf を実行させない——こういった「やってはいけないこと」を物理的に止められるのは PreToolUse だけ。
動作の仕組み:
- Claude が「
Readでファイルを読もう」と判断する - 実行前に PreToolUse フックが発火する
- あなたのスクリプトに、何のツールで何をしようとしているかが JSON で渡される
- スクリプトが判定する
exit 0→ 許可。Claude は操作を続行するexit 1→ ブロック。stderr に書いた理由が Claude にフィードバック される
- ブロックされた場合、Claude はその理由を読んで 別のアプローチを考える
例えば .env の読み込みをブロックした場合、Claude は「.env は読めないので、代わりに .env.example を参考にしましょう」と提案してくれます。ただ止まるのではなく、ブロック理由を理解して回避策を考える のが賢いポイントです。
🔹 PostToolUse — 実行後に自動処理
ツール実行が 完了した後 に発火します。
PreToolUse が「門番」なら、PostToolUse は 「後片付け係」 です。
使いどころ:
- コード整形: Claude が書いたファイルを Prettier / ESLint で自動フォーマット
- テスト実行: ファイルを書き換えた直後に、関連するテストを自動で走らせる
- ログ記録: 「いつ、どのファイルが、どう変更されたか」を記録する
- 通知: Slack に「Claude がコミットしました」と飛ばす
PostToolUse のスクリプトが失敗(exit 1)しても、操作自体は取り消されません。あくまで「実行後の追加処理」です。ここが PreToolUse との違い。
🔹 UserPromptSubmit — ユーザー入力時
ユーザーがプロンプトを送信したタイミングで発火します。
使いどころ:
- 操作ログ: 自分が何を指示したかをファイルに記録しておく(後で振り返りたい時に便利)
- キーワード警告: 「本番」「production」「delete」などの危険な単語が含まれていたら一拍置く
- テンプレート展開: 短縮記法を正式な指示に展開する
3 つの中では使用頻度が一番低いですが、チーム運用で操作の監査ログを残したい 場面では重宝します。
Advertisement
📝 設定方法
🔹 2 つの設定ファイル
| ファイル | 用途 | Git 管理 | 優先度 |
|---|---|---|---|
.claude/settings.json | チーム共有の設定 | する | 低 |
.claude/settings.local.json | 個人用の設定 | しない | 高(上書き) |
使い分けの具体例:
-
.claude/settings.json(チーム全員に適用):.envファイルの読み込みブロックrm -rfやgit push --forceのブロック- CI/CD 設定ファイルの書き込み保護
-
.claude/settings.local.json(個人の好み):- 自動フォーマット(Prettier 派 vs ESLint 派)
- 操作ログの保存
- 通知設定
両方に同じフックタイプの設定がある場合、local が優先 されます。
🔹 JSON の基本構造
{
"hooks": {
"PreToolUse": [
{
"matcher": "ツール名",
"command": "実行するコマンド"
}
],
"PostToolUse": [
{
"matcher": "ツール名",
"command": "実行するコマンド"
}
],
"UserPromptSubmit": [
{
"command": "実行するコマンド"
}
]
}
}
matcher にはツール名を指定します。Claude Code が使うツールは主に以下の通りです。
| matcher | 対象 | 例 |
|---|---|---|
Read | ファイル読み込み | .env や設定ファイルの保護 |
Write | ファイル新規作成 | 意図しないファイル生成の防止 |
Edit | ファイル編集 | 重要ファイルの書き換え防止 |
Bash | コマンド実行 | 危険コマンドのブロック |
Glob | ファイル検索 | 特定ディレクトリの探索防止 |
Grep | テキスト検索 | 秘密情報の検索防止 |
1 つのフックタイプに 複数のルールを配列で書ける ので、用途ごとにスクリプトを分けて管理できます。
🔹 フックスクリプトが受け取る JSON
フックスクリプトには、Claude が実行しようとしている ツールの入力情報が標準入力(stdin)で JSON として渡されます 。
#!/bin/bash
# stdin から JSON を読み取る
input=$(cat)
# 例:Read ツールの場合のJSON
# {
# "tool_name": "Read",
# "tool_input": {
# "file_path": "/home/user/project/.env"
# }
# }
# ファイルパスを取り出す
file_path=$(echo "$input" | jq -r '.tool_input.file_path // empty')
echo "Claude が $file_path を読もうとしています" >&2
Bash ツールの場合:
# {
# "tool_name": "Bash",
# "tool_input": {
# "command": "rm -rf dist/"
# }
# }
command=$(echo "$input" | jq -r '.tool_input.command // empty')
echo "Claude が実行しようとしているコマンド: $command" >&2
Edit ツールの場合:
# {
# "tool_name": "Edit",
# "tool_input": {
# "file_path": "src/index.ts",
# "old_string": "古い文字列",
# "new_string": "新しい文字列"
# }
# }
file_path=$(echo "$input" | jq -r '.tool_input.file_path // empty')
new_string=$(echo "$input" | jq -r '.tool_input.new_string // empty')
🔹 終了コードのルール
| 終了コード | 意味 | PreToolUse での効果 | PostToolUse での効果 |
|---|---|---|---|
0 | 成功 | 操作を 許可 | なし(正常終了) |
0 以外 | 失敗 | 操作を ブロック | 警告のみ(操作は取り消されない) |
重要: PreToolUse でブロックした場合、stderr に書いた内容が Claude にフィードバック されます。「なぜブロックしたか」を明確に書いておくと、Claude が適切な代替案を提案してくれます。
# 悪い例(理由がない)
exit 1
# 良い例(理由が明確)
echo "BLOCKED: .env ファイルは秘密情報を含むため読み込み禁止です。.env.example を参照してください。" >&2
exit 1
Advertisement
📝 実践レシピ集
ここからは、すぐにコピペして使えるレシピを紹介します。
それぞれ 「なぜ必要か」「何を防げるか」「コード」「動作の流れ」「カスタマイズのヒント」 をセットで解説します。
🔹 レシピ 1:.env ファイルの読み込みをブロック
なぜ必要か?
.env ファイルには以下のような情報が入っています。
DATABASE_URL=postgres://user:password@host:5432/mydb
OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxx
AWS_SECRET_ACCESS_KEY=xxxxxxxxxxxxxxxx
STRIPE_SECRET_KEY=sk_live_xxxxxxxxxxxxxxxx
Claude Code に「プロジェクトの構成を見て」と頼んだだけで、Claude が .env を読んでしまうことがあります 。Claude の会話ログにこの内容が残り、万が一ログが漏洩した場合、すべてのサービスの認証情報が流出 します。
たかが 1 ファイル、されど 1 ファイル。API キーの漏洩は 数分で数万円の被害 につながることもあります(AWS のクレデンシャル漏洩で仮想通貨マイニングに使われた事例は有名です)。
コード:
.claude/hooks/block-env-read.sh:
#!/bin/bash
input=$(cat)
file_path=$(echo "$input" | jq -r '.tool_input.file_path // empty')
# .env で始まるファイル名をすべてブロック(.env, .env.local, .env.production 等)
if [[ "$(basename "$file_path")" == .env* ]]; then
echo "BLOCKED: .env ファイルの読み込みは禁止されています。環境変数の参照には .env.example を使ってください。" >&2
exit 1
fi
.claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Read",
"command": "bash .claude/hooks/block-env-read.sh"
}
]
}
}
動作の流れ:
- あなた:「このプロジェクトの構成を見て」
- Claude:「まず
.envを読んで環境変数を確認します」 - Hook が発火 →
.envを含むパスを検出 →exit 1 - Claude:「
.envは読み込み禁止のようです。代わりに.env.exampleを確認しますね」
ブロック理由に「.env.example を使ってください」と書いてあるので、Claude は自動的に安全な代替案に切り替えてくれます。
カスタマイズのヒント:
.envだけでなく、credentials.json、secrets.yamlなども追加したい場合:
blocked_files=(".env" "credentials" "secrets" ".pem" ".key")
basename_file=$(basename "$file_path")
for blocked in "${blocked_files[@]}"; do
if [[ "$basename_file" == *"$blocked"* ]]; then
echo "BLOCKED: $basename_file は機密ファイルのため読み込み禁止です" >&2
exit 1
fi
done
🔹 レシピ 2:危険なコマンドをブロック
なぜ必要か?
Claude Code は Bash コマンドを実行できます。ほとんどの場合は適切なコマンドを使ってくれますが、文脈を誤解して破壊的なコマンドを提案することがあります 。
実際に起きうるシナリオ:
- 「ビルド成果物をきれいにして」→
rm -rf .(カレントディレクトリ全消し) - 「このブランチの変更をリモートに反映して」→
git push --force(他人のコミットを上書き) - 「テスト用のDBをリセットして」→
DROP DATABASE production;(本番DB消去)
許可プロンプトでは コマンドの中身を注意深く読む必要がありますが、長いコマンドだと見落としがち です。Hooks なら機械的にパターンマッチで弾けます。
コード:
.claude/hooks/block-dangerous.sh:
#!/bin/bash
input=$(cat)
command=$(echo "$input" | jq -r '.tool_input.command // empty')
# 危険パターンのリスト(必要に応じて追加・削除)
dangerous_patterns=(
"rm -rf /"
"rm -rf ."
"rm -fr"
"rm -rf ~"
"git push --force"
"git push -f"
"git reset --hard"
"git clean -fd"
"git checkout -- ."
"git branch -D"
"DROP TABLE"
"DROP DATABASE"
"TRUNCATE"
":(){ :|:& };:"
"mkfs"
"dd if="
"> /dev/sda"
)
for pattern in "${dangerous_patterns[@]}"; do
if [[ "$command" == *"$pattern"* ]]; then
echo "BLOCKED: 危険なコマンドパターン '$pattern' が含まれています。本当に必要な場合は、手動でターミナルから実行してください。" >&2
exit 1
fi
done
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"command": "bash .claude/hooks/block-dangerous.sh"
}
]
}
}
動作の流れ:
- あなた:「dist フォルダをきれいにして」
- Claude:「
rm -rf dist/を実行します」→ 通過(dist/限定なので安全) - あなた:「全部きれいにして」
- Claude:「
rm -rf .を実行します」→ ブロック! - Claude:「危険なコマンドがブロックされました。代わりに
git clean -nで確認してから削除しましょうか?」
ポイント: rm -rf dist/ のような 限定的な削除は通す が、rm -rf . のような 全消しはブロックする 。パターンの書き方でこの粒度を調整できます。
カスタマイズのヒント:
- プロジェクトによって追加すべきパターンは違います
- Rails なら
rails db:dropも追加 - Docker なら
docker system prune -afも追加 - npm なら
npm publishも追加(意図しない公開を防ぐ)
- Rails なら
🔹 レシピ 3:特定ディレクトリへの書き込みを禁止
なぜ必要か?
プロジェクトの中には「Claude に触らせたくないファイル」があります。
.github/workflows/: CI/CD パイプラインの設定。書き換えられると、デプロイが壊れる or 意図しないコードが本番に流れるterraform/、infrastructure/: インフラの定義。間違えるとサーバーが消えるdocker-compose.yml: コンテナ構成。ポート開放やボリューム設定を変えられると危険package.jsonの scripts: 悪意あるスクリプトが紛れ込む可能性
Claude Code は「良かれと思って」これらを書き換えることがあります。例えば「CI が遅いので最適化しますね」と言って workflow ファイルを変更し、結果的に テストをスキップする設定 にしてしまうことも。
コード:
.claude/hooks/protect-dirs.sh:
#!/bin/bash
input=$(cat)
file_path=$(echo "$input" | jq -r '.tool_input.file_path // .tool_input.file // empty')
# 保護するパスのリスト
protected_dirs=(
".github/workflows"
"infrastructure/"
"terraform/"
"docker-compose"
"Dockerfile"
".gitlab-ci"
"Jenkinsfile"
)
for dir in "${protected_dirs[@]}"; do
if [[ "$file_path" == *"$dir"* ]]; then
echo "BLOCKED: $dir は保護されたパスです。変更が必要な場合は手動で編集してください。" >&2
exit 1
fi
done
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write",
"command": "bash .claude/hooks/protect-dirs.sh"
},
{
"matcher": "Edit",
"command": "bash .claude/hooks/protect-dirs.sh"
}
]
}
}
カスタマイズのヒント:
保護対象は プロジェクトの性質 によって変わります。
| プロジェクト | 追加すべき保護パス |
|---|---|
| Web アプリ | nginx.conf, Caddyfile |
| モノレポ | 他チームのパッケージディレクトリ |
| モバイルアプリ | ios/, android/ のネイティブ部分 |
| ブログ | .github/workflows/ |
Advertisement
🔹 レシピ 4:ファイル編集後に自動フォーマット
なぜ必要か?
Claude Code が書くコードは概ね綺麗ですが、プロジェクトの Prettier/ESLint 設定とは微妙にずれる ことがあります。インデントがスペース 2 つなのに 4 つで書かれたり、セミコロンのありなしが混在したり。
手動で毎回 npx prettier --write を叩くのは面倒だし忘れがち。PostToolUse で ファイル保存のたびに自動フォーマット すれば、常にプロジェクトのスタイルガイドに準拠したコードになります。
コード:
.claude/hooks/auto-format.sh:
#!/bin/bash
input=$(cat)
file_path=$(echo "$input" | jq -r '.tool_input.file_path // .tool_input.file // empty')
# ファイルが存在しない場合(削除操作など)はスキップ
if [[ ! -f "$file_path" ]]; then
exit 0
fi
# JS/TS/JSX/TSX/CSS/JSON ファイルのみフォーマット
case "$file_path" in
*.js|*.ts|*.jsx|*.tsx|*.css|*.json|*.md|*.mdx)
npx prettier --write "$file_path" 2>/dev/null
;;
esac
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit",
"command": "bash .claude/hooks/auto-format.sh"
},
{
"matcher": "Write",
"command": "bash .claude/hooks/auto-format.sh"
}
]
}
}
動作の流れ:
- Claude が
src/components/Header.tsxを編集 - PostToolUse フックが発火
npx prettier --write src/components/Header.tsxが自動実行- ファイルがプロジェクトのスタイルガイド通りに整形される
Claude は整形後のファイルを次に読むときに見る ので、以降の作業も一貫したスタイルで進みます。
注意: PostToolUse は 実行のたびに走る ので、Prettier の起動時間(数百ms〜数秒)がそのまま Claude の操作ごとに加算されます。プロジェクトが大きくて Prettier が遅い場合は、対象ファイルの拡張子を絞る ことで対処してください。
🔹 レシピ 5:秘密情報の書き込みを検知
なぜ必要か?
Claude に「API 連携のサンプルコードを書いて」と頼んだ時、Claude がプレースホルダーではなく本物のキーっぽい文字列を生成する ことがあります。また、会話の中であなたが API キーを貼り付けてしまい、Claude がそれをコードにそのまま書き込むことも。
このフックは、ファイルに書き込まれようとしている内容に秘密情報のパターンが含まれていないかチェック します。
コード:
.claude/hooks/detect-secrets.sh:
#!/bin/bash
input=$(cat)
new_string=$(echo "$input" | jq -r '.tool_input.new_string // .tool_input.content // empty')
# 書き込み内容がない場合はスキップ
if [[ -z "$new_string" ]]; then
exit 0
fi
# 秘密情報のパターン(正規表現)
secret_patterns=(
"sk-[a-zA-Z0-9]{20,}" # OpenAI API key
"sk_live_[a-zA-Z0-9]+" # Stripe Secret key
"sk_test_[a-zA-Z0-9]+" # Stripe Test key
"AKIA[A-Z0-9]{16}" # AWS Access Key ID
"ghp_[a-zA-Z0-9]{36}" # GitHub Personal Access Token
"gho_[a-zA-Z0-9]{36}" # GitHub OAuth Token
"xoxb-[0-9]+-[a-zA-Z0-9]+" # Slack Bot Token
"-----BEGIN.*PRIVATE KEY-----" # SSH / TLS 秘密鍵
"AIza[a-zA-Z0-9_-]{35}" # Google API key
)
for pattern in "${secret_patterns[@]}"; do
if echo "$new_string" | grep -qE "$pattern"; then
echo "BLOCKED: 秘密情報のパターンが検出されました (pattern: $pattern)。API キーやトークンはコードにハードコードせず、環境変数を使ってください。" >&2
exit 1
fi
done
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit",
"command": "bash .claude/hooks/detect-secrets.sh"
},
{
"matcher": "Write",
"command": "bash .claude/hooks/detect-secrets.sh"
}
]
}
}
動作の流れ:
- あなた:「OpenAI の API を呼び出すコードを書いて。キーは sk-abc123… を使って」
- Claude がコードに
sk-abc123...をハードコードしようとする - Hook が発火 → OpenAI のキーパターンを検出 → ブロック
- Claude:「API キーのハードコードがブロックされました。代わりに
process.env.OPENAI_API_KEYを参照するコードを書きますね」
こうなってほしい姿 が自動的に実現します。
Advertisement
🔹 レシピ 6:操作ログを記録する
なぜ必要か?
Claude Code で長時間作業していると、「あれ、さっき何を変えたっけ?」となることがあります。特に複数ファイルを同時に編集している時、どのファイルをいつ変更したかの 全体像 を把握しにくくなります。
また、チーム開発で「Claude に何を触らせたか」の記録を残しておきたい場合にも役立ちます。
コード:
.claude/hooks/log-operations.sh:
#!/bin/bash
input=$(cat)
tool_name=$(echo "$input" | jq -r '.tool_name // "unknown"')
file_path=$(echo "$input" | jq -r '.tool_input.file_path // .tool_input.file // empty')
command=$(echo "$input" | jq -r '.tool_input.command // empty')
timestamp=$(date '+%Y-%m-%d %H:%M:%S')
log_file=".claude/operation.log"
if [[ -n "$file_path" ]]; then
echo "[$timestamp] $tool_name: $file_path" >> "$log_file"
elif [[ -n "$command" ]]; then
echo "[$timestamp] $tool_name: $command" >> "$log_file"
fi
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit",
"command": "bash .claude/hooks/log-operations.sh"
},
{
"matcher": "Write",
"command": "bash .claude/hooks/log-operations.sh"
},
{
"matcher": "Bash",
"command": "bash .claude/hooks/log-operations.sh"
}
]
}
}
ログの出力例:
[2026-04-13 14:30:22] Edit: src/components/Header.tsx
[2026-04-13 14:30:45] Bash: npm run build
[2026-04-13 14:31:10] Write: src/utils/helper.ts
[2026-04-13 14:31:33] Edit: src/pages/index.astro
[2026-04-13 14:32:01] Bash: git add -A && git commit -m "refactor header"
作業後にこのログを見返すだけで、Claude が何をしたかの全体像 が一目で分かります。.gitignore に .claude/operation.log を追加しておくことをおすすめします。
🔹 レシピ 7:特定ブランチへのコミットを禁止
なぜ必要か?
main や production ブランチに直接コミットさせたくない場合に。Claude Code は指示すれば git commit も git push もしてくれますが、ブランチの確認をせずにコミットしてしまう ことがあります。
コード:
.claude/hooks/protect-branch.sh:
#!/bin/bash
input=$(cat)
command=$(echo "$input" | jq -r '.tool_input.command // empty')
# git commit / git push を含むコマンドのみチェック
if [[ "$command" != *"git commit"* && "$command" != *"git push"* ]]; then
exit 0
fi
# 現在のブランチを取得
current_branch=$(git branch --show-current 2>/dev/null)
protected_branches=("main" "master" "production" "staging")
for branch in "${protected_branches[@]}"; do
if [[ "$current_branch" == "$branch" ]]; then
echo "BLOCKED: $branch ブランチへの直接コミット/プッシュは禁止されています。feature ブランチを作成してください。" >&2
exit 1
fi
done
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"command": "bash .claude/hooks/protect-branch.sh"
}
]
}
}
これで、うっかり main ブランチで作業していても コミットの瞬間にブロック されます。
Advertisement
📝 おすすめの初期設定セット
レシピが多くてどこから始めればいいか迷う方へ。段階的に導入する のがおすすめです。
🔹 レベル 1:最低限の安全装置(5 分で設定)
まずはこれだけ。事故の 9 割はこれで防げます 。
{
"hooks": {
"PreToolUse": [
{
"matcher": "Read",
"command": "bash .claude/hooks/block-env-read.sh"
},
{
"matcher": "Bash",
"command": "bash .claude/hooks/block-dangerous.sh"
}
]
}
}
必要なスクリプト: block-env-read.sh(レシピ 1)+ block-dangerous.sh(レシピ 2)
🔹 レベル 2:品質の自動維持(+5 分)
レベル 1 に自動フォーマットと秘密情報検知を追加。
{
"hooks": {
"PreToolUse": [
{
"matcher": "Read",
"command": "bash .claude/hooks/block-env-read.sh"
},
{
"matcher": "Bash",
"command": "bash .claude/hooks/block-dangerous.sh"
},
{
"matcher": "Edit",
"command": "bash .claude/hooks/detect-secrets.sh"
},
{
"matcher": "Write",
"command": "bash .claude/hooks/detect-secrets.sh"
}
],
"PostToolUse": [
{
"matcher": "Edit",
"command": "bash .claude/hooks/auto-format.sh"
},
{
"matcher": "Write",
"command": "bash .claude/hooks/auto-format.sh"
}
]
}
}
🔹 レベル 3:チーム運用(+10 分)
レベル 2 にディレクトリ保護・ブランチ保護・操作ログを追加。チームでの Claude Code 運用に。
ここまで来ると、Claude Code を「何も考えずに Y 連打しても安全」な状態 に持っていけます。最初に仕組みを作る手間はかかりますが、その後の何百時間もの作業が格段に安全になります。
Advertisement
📝 フックのデバッグ方法
「フックを設定したのに動かない」「ブロックされるはずなのに通ってしまう」——そんな時のトラブルシューティングです。
🔹 まずスクリプト単体でテストする
フックスクリプトは普通のシェルスクリプトなので、Claude Code を通さずに直接テスト できます。
# テスト用の JSON を stdin に渡して実行
echo '{"tool_name":"Read","tool_input":{"file_path":"/home/user/.env"}}' | bash .claude/hooks/block-env-read.sh
# 終了コードを確認(1 ならブロック成功)
echo $?
🔹 デバッグ出力を追加する
スクリプトに >&2 で stderr に出力を入れると、Claude Code のログに表示されます。
echo "DEBUG: file_path = $file_path" >&2
echo "DEBUG: command = $command" >&2
🔹 よくあるミス
| 症状 | 原因 | 対処 |
|---|---|---|
| フックが動かない | jq がインストールされていない | jq --version で確認、なければインストール |
| フックが動かない | スクリプトに実行権限がない | chmod +x .claude/hooks/*.sh |
| フックが動かない | パスが間違っている | command のパスをプロジェクトルートからの相対パスで確認 |
| 全部ブロックされる | jq のフィルタが常に空文字を返している | `echo “$input” |
| 一部だけ通り抜ける | パターンマッチが不完全 | 大文字小文字、スペースの違いをチェック |
📝 注意点
🔹 フックスクリプトには jq が必要
この記事のレシピはすべて jq(JSON パーサー)を使っています。ほとんどの開発環境にはインストール済みですが、なければ追加が必要です。
# macOS
brew install jq
# Ubuntu / Debian
sudo apt install jq
# Windows (Scoop)
scoop install jq
# Windows (Chocolatey)
choco install jq
jq を使わずに grep や sed だけで書くこともできますが、JSON の構造をパースするには jq が圧倒的に楽です。
🔹 フックの実行時間に注意
フックスクリプトは ツール実行のたびに走る ので、重い処理を入れると Claude Code の体感速度が落ちます。
| 処理 | 所要時間 | 影響 |
|---|---|---|
| jq でパターンマッチ | 数 ms | ほぼなし |
| Prettier(小さいファイル) | 200〜500 ms | 許容範囲 |
| ESLint(プロジェクト全体) | 数秒〜十数秒 | 遅すぎ |
| テストスイート全体 | 数十秒〜数分 | 論外 |
目安: PostToolUse のスクリプトは 1 秒以内 に終わるようにしましょう。重い処理は「コミット時だけ」「特定のファイルだけ」と条件を絞ることで対処できます。
🔹 settings.json はチーム共有、settings.local.json は個人用
| 観点 | settings.json | settings.local.json |
|---|---|---|
| Git 管理 | される | されない |
| チーム共有 | ◯ | ✕ |
| 用途 | セキュリティ・チーム共通ルール | 個人の好み・環境依存 |
| 例 | .env ブロック、危険コマンドブロック | 自動フォーマット、操作ログ |
チームで使う場合: settings.json と hooks/ ディレクトリをリポジトリに含め、PR レビューの対象にしましょう。新しいメンバーが Claude Code を使い始めた時点で、自動的にガードレールが効く状態が理想です。
Advertisement
📝 よくある質問
🔹 Q: Hooks を設定したら Claude Code が遅くなりませんか?
PreToolUse のパターンマッチ程度なら体感できないレベル(数 ms)です。PostToolUse で Prettier を走らせる場合は 200〜500 ms ほど加算されますが、コードが常にきれいになるメリットの方が大きいです。重い処理さえ避ければ問題ありません。
🔹 Q: フックをバイパスする方法はありますか?
Claude Code 自体がフックをバイパスすることは できません 。これが Hooks の強みです。ただし、あなた自身がターミナルで直接コマンドを実行すれば当然フックは関係ありません。「Claude にはやらせないけど、自分ではやりたい」という操作は手動で実行できます。
🔹 Q: Windows でも使えますか?
使えます。bash スクリプトは Git Bash や WSL で実行されます。Windows 環境の場合、command を以下のように書くとよいでしょう。
{
"command": "bash .claude/hooks/block-env-read.sh"
}
Git for Windows がインストールされていれば、bash コマンドは使えます。
🔹 Q: 1 つのツールに複数のフックを設定できますか?
できます。配列に複数のオブジェクトを並べるだけです。
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"command": "bash .claude/hooks/block-dangerous.sh"
},
{
"matcher": "Bash",
"command": "bash .claude/hooks/protect-branch.sh"
}
]
}
}
すべてのフックが exit 0 を返した場合のみ操作が許可 されます。1 つでも exit 1 を返せばブロック。
📝 さいごに
Claude Code Hooks は 「人間の注意力」に頼らず、仕組みで安全を担保する ための機能です。
「設定が面倒」「シェルスクリプトを書くのがだるい」——気持ちは分かります。でも、.env の漏洩で API キーが流出したら? rm -rf . でプロジェクトが消えたら? git push --force でチームの 1 週間分の作業が消えたら?
事故が起きてからの対応コスト と比べたら、最初の 10 分でフックを設定するコスト は無に等しいです。
まずは レシピ 1(.env ブロック)だけでも 入れてみてください。それだけで「Claude Code が .env を読もうとしたらブロックしてくれる」という安心感が手に入ります。
前回の git worktree で並列開発する記事 と合わせて、Claude Code を 安全に、効率的に 使いこなしていきましょう。
🚨 危険
ご注意ください:本記事に掲載しているスクリプトには、AI によって生成されたコードが含まれています。あくまで Hooks の考え方と導入の参考例であり、これらをそのまま使えば完璧なセキュリティ対策になるわけではありません 。実際のプロジェクトに導入する際は、ご自身の環境や要件に合わせて内容を検証・カスタマイズしたうえでご利用ください。セキュリティに関わる設定は、最終的にはご自身の責任で判断をお願いいたします。
Advertisement
関連記事
Claude Code を2〜3個同時に走らせる|git worktree で並列開発する海外流儀
Claude Codeで複数エージェントを作って議論させる方法|AI同士のディベートが面白い
Claude Chat と Claude Code の違いを徹底比較|どっちを使うべきか用途別に解説
【2026年3月最新版】ChatGPT vs Claude vs Gemini 徹底比較 | 料金・機能・使い分けを解説
Midjourney v7 プロンプトの書き方・コツ完全ガイド|パラメーター一覧と ChatGPT・Gemini での生成法
ChatGPT・ClaudeでSuno AI用の歌詞とプロンプトを作る方法|AI作曲の歌詞づくりコツ
【2026年3月最新版】Suno AI タグ・プロンプト完全リファレンス|メタタグ一覧と使い方
Steamセールの賢い買い方と積みゲー対策|後悔しないゲームの買い方
マイクラのMOD管理が最強のツール Modrinth とは?MOD導入が一瞬になる最強ランチャーの導入方法・使い方