アフィリエイト広告を利用しています

# 【実践レポート】Claude Code を安全に自走させる「Claude Code Guard」を作った話

Claude Code を bypassPermissions で自走させたいけど、勝手にファイルを消されたり、git push されたり、データを外部送信されたら困る……ということ、ありませんか?

結論からお伝えすると、cage + hooks + settings.json の3層構造で「開いているフォルダの中だけ自由に操作できる」環境を作れます。 セットアップスクリプト1発で導入できるようにClaude codeと一緒に作ったので、GitHub で公開しました。

Claude Code Guard(GitHub)

この記事では、なぜこの設定が必要なのか、どんな仕組みで守っているのか、セットアップ方法まで、まるっとお伝えします。

なお、この記事はこちらを参考にさせていただきました。感謝です。

202602個人的claude code設定

bypassPermissions の「自走」は便利、でもドキドキする

Claude Code には bypassPermissions というモードがあります。これを有効にすると、いちいち「このコマンド実行していいですか?」と聞かれなくなるので、作業がめちゃくちゃ快適になります。

で、快適なんですが……ドキドキするんですよね。

だって、Claude が自由にコマンドを叩けるということは、こんなことも起こりうるわけです。

  • プロジェクトと関係ないディレクトリのファイルを消す
  • 勝手に git push してしまう
  • curl でデータを外部に送信してしまう
  • 環境変数から API キーを読み取って……あっ、考えたくない

「Claude Code 本体にも sandbox 機能あるやん」と思うかもしれません。ところが、bypassPermissions の状態だと Claude が自力で sandbox を突破するケースが報告されています。なんでやねん。

じゃ、どうすれば、いいのか?

Claude の外側から囲む、3層のガードレール

Claude 本体の機能に頼るのではなく、外側のツールで防御するというアプローチです。3つのレイヤーで守ります。

レイヤーツール役割
1cageOS レベルでファイルアクセスを制限
2hooksコマンド実行前に中身を検証
3settings.jsondeny リストで二重防御

順番に見ていきましょう。

レイヤー1: cage でフォルダの外に出さない

cage は、macOS 上でプロセスのファイルアクセスを制限できるサンドボックスツールです。

cage claude で起動すると、Claude Code が触れるディレクトリをこちらで指定できます。

# ~/.config/cage/presets.yml
presets:
  base:
    allow:
      - "."                    # カレントディレクトリのみ!
      - "/private/tmp"         # macOS tmpディレクトリ
      - "/var/folders"         # macOS mktemp
      - "$HOME/Library/Caches" # macOS アプリキャッシュ
      - "$HOME/.npm"           # npm キャッシュ
      - "$HOME/.local/share"   # CLIツール用データ

  claude-code:
    allow:
      - "$CLAUDE_CONFIG_DIR"   # ~/.claude(設定・履歴)

ポイントは .(カレントディレクトリ)だけが自由という点です。 それ以外は読み込みすらできません。

cage は OS レベルで制限をかけるので、Claude が「sandbox を突破してやろう」と思っても突破できません。ほっ。

レイヤー2: hooks でコマンドを検証する

cage でファイルアクセスは守れますが、ネットワーク送信や危険なコマンドは別途止める必要があります。

Claude Code の PreToolUse フック機能を使って、Bash コマンドと WebFetch の実行前に中身をチェックする validate-bash.sh を作りました。

止めるもの一覧

けっこうな量を止めています。5つのカテゴリに分けて紹介します。

間接実行の防止(パターン回避対策)

Claude は賢いので、直接的なコマンドを禁止すると迂回しようとすることがあります。なので先回りして塞ぎます。

  • eval — 文字列をコマンドとして実行(全チェックを回避できてしまう)
  • | bash / | sh — パイプ経由のシェル実行
  • base64 -d — エンコードされたコマンドの復号
  • $VAR push — 変数経由でコマンドを組み立てる
  • source — 外部スクリプトの読み込み実行

ネットワーク送信の防止

データの外部送信を防ぎます。

  • curl の POST / PUT / PATCH / DELETE
  • curl の GET でも $(...) を含む場合(URL にデータを埋め込む手口)
  • wget --post-data / --post-file
  • Python / Node.js の送信系 API(requests.posturlopenfetch 等)
  • nc / netcat

スクリプトファイル経由の送信も検出

ここでハマりました。 インラインのコマンドだけ止めても、Claude が Python スクリプトを書いてから python3 script.py で実行すると、スクリプト内のネットワーク送信コードは検出できません。

というわけで、python3 ファイル名.pynode ファイル名.js の実行を検出したら、そのファイルの中身をスキャンしてrequestsurllibhttp.clientsocket 等のネットワーク関連コードが含まれていないかチェックするようにしました。

# Python スクリプトファイルの実行を検出してスキャン
if echo "$command" | grep -qE '\bpython3?\s+[^-]'; then
  script_file=$(echo "$command" | grep -oE '\bpython3?\s+(\S+)' | head -1 | grep -oE '\S+$')
  if [[ -n "$script_file" && -f "$script_file" ]]; then
    matched=$(grep -nE "$NETWORK_PATTERNS_PY" "$script_file" 2>/dev/null | head -5)
    if [[ -n "$matched" ]]; then
      deny "Python スクリプト '${script_file}' にネットワーク送信コードが検出されました: ${matched}"
    fi
  fi
fi

該当行が具体的に表示されるので、何が引っかかったか一目でわかります。

WebFetch ツールの制御

Claude の WebFetch ツール(Bash を経由しない)も、URL にデータを埋め込んで外部送信される可能性があります。クエリパラメータが200文字を超える場合や、単一パラメータの値が100文字を超える場合は拒否します。

通常のドキュメント閲覧はそのまま通るので、開発作業には影響ありません。

環境変数の読み取り防止

  • env / printenv — 全環境変数の一覧表示
  • export -p — 全 export 変数の一覧
  • set(引数なし)— シェル変数の一覧

API キーやトークンが環境変数に入っていることは多いので、一括表示は止めています。

危険コマンドの防止

  • git push — 意図しないプッシュを防止
  • git add -A / git add . — ファイル名を個別に指定させる
  • sed / awk — Claude の Edit ツールを使わせる
  • rm -rf /... — カレントディレクトリ外の削除防止

ブロック時はユーザーに確認してもらう

すべての拒否メッセージに「実行が必要な場合は、コマンド全文をユーザーに提示し、手動実行を依頼してください。」という指示が自動で付きます。

なので、業務上 curl -X POST が必要なときの流れはこうなります。

  1. Claude が POST コマンドを実行しようとする
  2. hooks が拒否
  3. Claude がコマンド全文をユーザーに提示する
  4. ユーザーが中身を確認して、自分で実行する

Claude が勝手にデータを送信することはなく、かつ業務は止まらない、という感じです。ふぅ。

レイヤー3: settings.json で二重防御

hooks でカバーしていますが、settings.json の deny リストでも重要なものは止めています。ベルトとサスペンダー方式ですね。

{
  "permissions": {
    "defaultMode": "bypassPermissions",
    "deny": [
      "Bash(git push:*)",
      "Bash(git add -A:*)",
      "Bash(git add --all:*)"
    ]
  }
}

セットアップは1コマンド

ここまでの設定、手作業で全部やるのはしんどいですよね。setup.sh で自動化しました。

git clone https://github.com/mono96/claude-code-guard.git
cd claude-code-guard
chmod +x setup.sh
./setup.sh

setup.sh がやってくれること:

  • cage が未インストールなら brew install で自動インストール
  • ~/.config/cage/presets.yml を配置
  • ~/.claude/hooks/validate-bash.sh を配置して実行権限を付与
  • ~/.claude/settings.json を既存設定とマージ(env や enabledPlugins を保持)
  • 全ステップの結果を色付きで表示

既存の設定ファイルがある場合は自動でバックアップしてからマージするので、安心です。

起動方法

cage claude

これだけです。パラメータもそのまま渡せます。

cage claude --continue
cage claude -p "テストを実行して"

毎回 cage を付けるのが面倒なら、.zshrc にこう書いておけば claude コマンドだけで cage 経由になります。

function claude() {
  cage claude "$@"
}

残っている限界(正直に書きます)

完璧ではないので、把握しておいてほしい点が2つあります。

1. 個別の環境変数は読める

env は止めていますが、echo $SECRET_KEY のように変数名を直接指定されると止められません。完全に塞ぐと echo $PATH のような正常な操作まで壊れるので、ここはトレードオフとして許容しています。

2. WebSearch は未対策

検索クエリに機密情報を埋め込む可能性はゼロではありませんが、実用上のリスクは低いと判断しています。

カスタマイズ

MCP ツールを追加したとき

そのツールがデータを保存するディレクトリを cage の設定に追加してください。

presets:
  claude-code:
    allow:
      - "$CLAUDE_CONFIG_DIR"
      - "$HOME/.serena"  # 例: serena を使う場合

拒否コマンドを追加したいとき

validate-bash.sh にパターンを追加するだけです。

# 例: docker を禁止
if echo "$command" | grep -qE '\bdocker\b'; then
  deny "docker は禁止です。理由を書く。"
fi

まとめ

Claude Code を bypassPermissions で自走させるなら、Claude の外側からガードレールを張るのが安全です。

  • cage で OS レベルのファイルアクセス制限 → カレントディレクトリの中だけ自由
  • hooks でコマンド・ネットワーク・環境変数を検証 → 危険な操作は理由付きで拒否
  • settings.json で deny リスト → 二重防御

拒否されたコマンドも、ユーザーが確認して手動実行すれば業務は止まりません。

セットアップスクリプト1発で導入できるので、お試しください。

Claude Code Guard(GitHub)

この記事を書いた人

大東 信仁

カンパチが好きです。

プロフィールはこちら

10月14日開催 参加者募集中
(画像をタップ→詳細へ)

ミッションナビゲート モニター
(画像をタップ→詳細へ)

広告