Claude Code Hooks で AI の暴走を止める|.env 読み込み拒否から自動 lint まで

#Claude Code#セキュリティ#開発効率#AI
※本ページはプロモーションが含まれています
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

AD SPACE

📝 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

AD SPACE

📝 3 種類のフック

Hooks には 3 つのタイミングがあります。

フックタイミング主な用途
PreToolUseツール実行の 危険な操作のブロック・確認
PostToolUseツール実行の 自動 lint・フォーマット・通知
UserPromptSubmitユーザーが入力を送信した時入力の前処理・ログ記録

🔹 PreToolUse — 実行前にブロックできる

最も重要なフック です。Claude がツール(Read, Write, Edit, Bash など)を使おうとした瞬間に発火します。

なぜこれが一番大事か?

他の 2 つのフックは「事後処理」や「入力処理」ですが、PreToolUse だけは 「実行させない」ことができる からです。.env を読ませない、rm -rf を実行させない——こういった「やってはいけないこと」を物理的に止められるのは PreToolUse だけ。

動作の仕組み:

  1. Claude が「Read でファイルを読もう」と判断する
  2. 実行前に PreToolUse フックが発火する
  3. あなたのスクリプトに、何のツールで何をしようとしているかが JSON で渡される
  4. スクリプトが判定する
    • exit 0 → 許可。Claude は操作を続行する
    • exit 1 → ブロック。stderr に書いた理由が Claude にフィードバック される
  5. ブロックされた場合、Claude はその理由を読んで 別のアプローチを考える

例えば .env の読み込みをブロックした場合、Claude は「.env は読めないので、代わりに .env.example を参考にしましょう」と提案してくれます。ただ止まるのではなく、ブロック理由を理解して回避策を考える のが賢いポイントです。

🔹 PostToolUse — 実行後に自動処理

ツール実行が 完了した後 に発火します。

PreToolUse が「門番」なら、PostToolUse は 「後片付け係」 です。

使いどころ:

  • コード整形: Claude が書いたファイルを Prettier / ESLint で自動フォーマット
  • テスト実行: ファイルを書き換えた直後に、関連するテストを自動で走らせる
  • ログ記録: 「いつ、どのファイルが、どう変更されたか」を記録する
  • 通知: Slack に「Claude がコミットしました」と飛ばす

PostToolUse のスクリプトが失敗(exit 1)しても、操作自体は取り消されません。あくまで「実行後の追加処理」です。ここが PreToolUse との違い。

🔹 UserPromptSubmit — ユーザー入力時

ユーザーがプロンプトを送信したタイミングで発火します。

使いどころ:

  • 操作ログ: 自分が何を指示したかをファイルに記録しておく(後で振り返りたい時に便利)
  • キーワード警告: 「本番」「production」「delete」などの危険な単語が含まれていたら一拍置く
  • テンプレート展開: 短縮記法を正式な指示に展開する

3 つの中では使用頻度が一番低いですが、チーム運用で操作の監査ログを残したい 場面では重宝します。

Advertisement

AD SPACE

📝 設定方法

🔹 2 つの設定ファイル

ファイル用途Git 管理優先度
.claude/settings.jsonチーム共有の設定する
.claude/settings.local.json個人用の設定しない高(上書き)

使い分けの具体例:

  • .claude/settings.json(チーム全員に適用):

    • .env ファイルの読み込みブロック
    • rm -rfgit 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

AD SPACE

📝 実践レシピ集

ここからは、すぐにコピペして使えるレシピを紹介します。

それぞれ 「なぜ必要か」「何を防げるか」「コード」「動作の流れ」「カスタマイズのヒント」 をセットで解説します。


🔹 レシピ 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"
      }
    ]
  }
}

動作の流れ:

  1. あなた:「このプロジェクトの構成を見て」
  2. Claude:「まず .env を読んで環境変数を確認します」
  3. Hook が発火.env を含むパスを検出 → exit 1
  4. Claude:「.env は読み込み禁止のようです。代わりに .env.example を確認しますね」

ブロック理由に「.env.example を使ってください」と書いてあるので、Claude は自動的に安全な代替案に切り替えてくれます。

カスタマイズのヒント:

  • .env だけでなく、credentials.jsonsecrets.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"
      }
    ]
  }
}

動作の流れ:

  1. あなた:「dist フォルダをきれいにして」
  2. Claude:「rm -rf dist/ を実行します」→ 通過dist/ 限定なので安全)
  3. あなた:「全部きれいにして」
  4. Claude:「rm -rf . を実行します」→ ブロック!
  5. Claude:「危険なコマンドがブロックされました。代わりに git clean -n で確認してから削除しましょうか?」

ポイント: rm -rf dist/ のような 限定的な削除は通す が、rm -rf . のような 全消しはブロックする 。パターンの書き方でこの粒度を調整できます。

カスタマイズのヒント:

  • プロジェクトによって追加すべきパターンは違います
    • Rails なら rails db:drop も追加
    • Docker なら docker system prune -af も追加
    • npm なら npm publish も追加(意図しない公開を防ぐ)

🔹 レシピ 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

AD SPACE

🔹 レシピ 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"
      }
    ]
  }
}

動作の流れ:

  1. Claude が src/components/Header.tsx を編集
  2. PostToolUse フックが発火
  3. npx prettier --write src/components/Header.tsx が自動実行
  4. ファイルがプロジェクトのスタイルガイド通りに整形される

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"
      }
    ]
  }
}

動作の流れ:

  1. あなた:「OpenAI の API を呼び出すコードを書いて。キーは sk-abc123… を使って」
  2. Claude がコードに sk-abc123... をハードコードしようとする
  3. Hook が発火 → OpenAI のキーパターンを検出 → ブロック
  4. Claude:「API キーのハードコードがブロックされました。代わりに process.env.OPENAI_API_KEY を参照するコードを書きますね」

こうなってほしい姿 が自動的に実現します。

Advertisement

AD SPACE

🔹 レシピ 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:特定ブランチへのコミットを禁止

なぜ必要か?

mainproduction ブランチに直接コミットさせたくない場合に。Claude Code は指示すれば git commitgit 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

AD SPACE

📝 おすすめの初期設定セット

レシピが多くてどこから始めればいいか迷う方へ。段階的に導入する のがおすすめです。

🔹 レベル 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

AD SPACE

📝 フックのデバッグ方法

「フックを設定したのに動かない」「ブロックされるはずなのに通ってしまう」——そんな時のトラブルシューティングです。

🔹 まずスクリプト単体でテストする

フックスクリプトは普通のシェルスクリプトなので、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 を使わずに grepsed だけで書くこともできますが、JSON の構造をパースするには jq が圧倒的に楽です。

🔹 フックの実行時間に注意

フックスクリプトは ツール実行のたびに走る ので、重い処理を入れると Claude Code の体感速度が落ちます。

処理所要時間影響
jq でパターンマッチ数 msほぼなし
Prettier(小さいファイル)200〜500 ms許容範囲
ESLint(プロジェクト全体)数秒〜十数秒遅すぎ
テストスイート全体数十秒〜数分論外

目安: PostToolUse のスクリプトは 1 秒以内 に終わるようにしましょう。重い処理は「コミット時だけ」「特定のファイルだけ」と条件を絞ることで対処できます。

🔹 settings.json はチーム共有、settings.local.json は個人用

観点settings.jsonsettings.local.json
Git 管理されるされない
チーム共有
用途セキュリティ・チーム共通ルール個人の好み・環境依存
.env ブロック、危険コマンドブロック自動フォーマット、操作ログ

チームで使う場合: settings.jsonhooks/ ディレクトリをリポジトリに含め、PR レビューの対象にしましょう。新しいメンバーが Claude Code を使い始めた時点で、自動的にガードレールが効く状態が理想です。

Advertisement

AD SPACE

📝 よくある質問

🔹 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