2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【コピペOK】Claude Codeのデータ削除・危険コマンドを防止するHooksの使い方

Posted at

こんにちは、とまだです。

Claude Code を使って Bash コマンドを自動化するとき、気になることがありますよね。「AIが勝手に危険なコマンドを実行してしまわないだろうか」「大事なファイルを誤って削除されたら困る」という不安です。

実はこの不安は、Claude Code の Hooks という機能を使うことで解決できます。Hooks を使えば、AI が実行しようとするコマンドをあらかじめチェックして、危険な操作なら実行を ブロック することが可能です。

この記事では、Claude Code の Hooks を使ったセキュリティ対策の方法を、基本から実装まで丁寧に解説します。

著者について

とまだ

  • Claude Code・Cursor・Codex などAI駆動開発の実践者
  • 本業はフリーランスエンジニア、ならびに法人向けAI駆動開発の導入コンサルタント
  • AI駆動開発を学ぶ方が集まる Vibe Coding Studio コミュニティを運営
  • UdemyでAI駆動開発を学べる複数のベストセラー講座を展開
  • 東京AI祭プレイベントに登壇
  • プログラミングスクール講師として100名以上に指導経験

SNS

忙しい人のために要約

  • Claude Code の Hooks は、AI が実行するツール(Bash など)を実行前にチェックする機能
  • safe_command.py というセキュリティスクリプトを作成して、危険なコマンドを防げる
  • 禁止コマンドパターン(sudo、rm -rf など)と保護ディレクトリ(/etc、ホームディレクトリなど)を設定可能
  • 実装は 4 手順:ディレクトリ作成 → スクリプト作成 → 設定追加 → テスト

Claude Code の Hooks とは

Claude Code を使っていると、AI が自動的にファイルを作成したり、Bash コマンドを実行したりします。これらの操作を「ツール実行」と呼びますね。

Hooks は、こうしたツール実行を実行前に チェック・制御 する仕組みです。例えるなら、レストランのキッチンでシェフが料理を提供する前に「この食材は新鮮か?」「調理法は正しいか?」と確認するようなものです。Bash ツールが実行される直前に検証スクリプトを走らせることで、危険なコマンドが実行されるのを防げるわけです。

特に重要なのが PreToolUse(ツール使用前)フックです。これは、ツール実行の直前(Pre)に、任意のスクリプトを走らせられます。つまり、Bash ツールが実行される前に、このスクリプトでコマンドを先読みしてチェックすることができるということです。

公式の例:bash_command_validator_example.py

Anthropic 公式は、すでに参考になるコード例を提供しています。bash_command_validator_example.py という例では、grep コマンドを ripgrep(rg)に置き換えたり、find コマンドを使う場合に警告を出したりしています。

この公式例を参考にしながら、今回は セキュリティ対策に特化した フックスクリプトを作成していきます。

セキュリティフックの実装手順

では、危険なコマンドをブロックするセキュリティフックを実装する方法を見ていきましょう。やることは 4 つの手順に分かれていますが、どれも難しくありません。

手順 1:ホームディレクトリに hooks ディレクトリを作成

まず、Claude Code の設定が置かれるホームディレクトリに、フックスクリプトを格納するディレクトリを作成します。

mkdir -p ~/.claude/hooks

このディレクトリの中に、セキュリティスクリプトを置いていきます。

手順 2:safe_command.py を作成・編集

次に、実際のセキュリティチェックを行う Python スクリプト safe_command.py を作成します。このスクリプトが、実行しようとしているコマンドをチェックする役割を果たします。

touch ~/.claude/hooks/safe_command.py
chmod +x ~/.claude/hooks/safe_command.py

作成したファイルを編集して、以下のコードを貼り付けます。

このスクリプトは、「危険なコマンドのパターンをリスト化して、実行前にチェックする」というシンプルな考え方で成り立っています。複雑に見えるかもしれませんが、段階的に仕組みを理解していけば大丈夫です。

#!/usr/bin/env python3
import json
import re
import sys
import os

# 削除を禁止するディレクトリ(配下も含む)
PROTECTED_DIRS = [
    "/etc",
    "/usr",
    "/var",
    "/opt",
    "/home",
    "guard_test",  # テスト用の保護されたディレクトリ
]

# 禁止するコマンドパターン(正規表現で危険なコマンドを指定)
FORBIDDEN_COMMANDS = [
    r"\bsudo\b",
    r"\brm\s+(-[a-zA-Z]*r[a-zA-Z]*f|-[a-zA-Z]*f[a-zA-Z]*r)\b",
    r"\brm\s+.*\*",
    r"\bchmod\s+777\b",
    r"curl.*\|\s*(bash|sh)",
    r"wget.*\|\s*(bash|sh)",
    r":\(\)",
    r"\bdd\s+.*of=/dev/",
    r"\bmkfs\b",
    r">\s*/dev/(sd|hd|nvme)",
]

# 削除系コマンドのパターン
DELETE_COMMANDS = [
    r"\brm\b",
    r"\brmdir\b",
    r"\bunlink\b",
    r"\bfind\b.*-delete",
    r"\bfind\b.*-exec\s+rm",
    r"\bxargs\s+rm",
]


def _check_protected_directory_deletion(command: str) -> bool:
    """保護されたディレクトリへの削除操作をチェック"""
    has_delete = any(re.search(pattern, command, re.IGNORECASE)
                     for pattern in DELETE_COMMANDS)
    if not has_delete:
        return False

    for protected_dir in PROTECTED_DIRS:
        if re.search(re.escape(protected_dir), command):
            return True

    return False


def _check_home_directory_deletion(command: str) -> bool:
    """ホームディレクトリ直下の削除操作をチェック"""
    home = os.path.expanduser("~")

    has_delete = any(re.search(pattern, command, re.IGNORECASE)
                     for pattern in DELETE_COMMANDS)
    if not has_delete:
        return False

    home_patterns = [
        rf"{re.escape(home)}/[^/\s]+(?:\s|$)",
        r"~/[^/\s]+(?:\s|$)",
        r"\$HOME/[^/\s]+(?:\s|$)",
    ]

    return any(re.search(pattern, command) for pattern in home_patterns)


def _validate_command(command: str) -> list[str]:
    issues = []

    if _check_protected_directory_deletion(command):
        issues.append("保護されたディレクトリの削除は禁止されています")

    if _check_home_directory_deletion(command):
        issues.append("ホームディレクトリ直下の削除は禁止されています")

    for pattern in FORBIDDEN_COMMANDS:
        if re.search(pattern, command, re.IGNORECASE):
            issues.append(f"禁止されたコマンドパターンが検出されました: {pattern}")
            break

    return issues


def main():
    try:
        input_data = json.load(sys.stdin)
    except json.JSONDecodeError as e:
        print(f"Error: {e}", file=sys.stderr)
        sys.exit(1)

    if input_data.get("tool_name") != "Bash":
        sys.exit(0)

    command = input_data.get("tool_input", {}).get("command", "")
    if not command:
        sys.exit(0)

    issues = _validate_command(command)
    if issues:
        print("⚠️ 危険なコマンドが検出されました:", file=sys.stderr)
        for issue in issues:
            print(f"{issue}", file=sys.stderr)
        sys.exit(2)


if __name__ == "__main__":
    main()

では、このコードの主要な部分を説明していきます。

  • PROTECTED_DIRS:削除を禁止するディレクトリのリストを定義
  • FORBIDDEN_COMMANDS:禁止するコマンドパターンを正規表現で指定
  • DELETE_COMMANDS:削除系コマンドのパターンを定義
  • _validate_command():実行されようとしているコマンドをチェック
  • main():JSON 形式の入力を受け取り、チェック結果に応じて終了コードを返す

詳しい仕組みは、後ほどの解説セクションで一つずつ丁寧に説明していきますので、今は各部分の役割を頭に入れておいてください。

手順 3:Claude Code の設定を編集

次に、Claude Code の設定ファイル ~/.claude/settings.json を編集して、先ほど作成したフックスクリプトを登録します。

vi ~/.claude/settings.json

settings.json に以下の内容を追加します。既存の内容(Permissions など)は残しておいて OK です。

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "python3 ~/.claude/hooks/safe_command.py"
          }
        ]
      }
    ]
  }
}

このプロトコルは「Bash ツールが実行される直前に、python3 ~/.claude/hooks/safe_command.py を走らせよ」という意味です。

手順 4:テスト用ディレクトリを作成

それでは、フックが正しく動作するか確認するためのテスト環境を作成しましょう。safe_command.py では、guard_test ディレクトリを保護対象に設定しているので、これを実際に作成します。

mkdir -p ~/guard_test/subdir
echo "test file" > ~/guard_test/test.txt
echo "sub file" > ~/guard_test/subdir/file.txt

これで準備が整いました。

守られるセキュリティ対策(仕組みの解説)

ここからは、先ほど作成したスクリプトが どのようにセキュリティを守っているのか を具体的に解説します。複雑に見えるかもしれませんが、実際には「危険な操作を事前に防ぐ」という同じ原理で統一されています。一つずつ見ていきましょう。

禁止されるコマンドパターン

safe_command.py の FORBIDDEN_COMMANDS リストには、危険なコマンドパターンが正規表現で定義されています。それぞれどんな危険があるのかを見ていきましょう。

sudo コマンド

r"\bsudo\b",

sudo を使うと、システム権限で任意のコマンドを実行できてしまいますね。これは非常に危険なわけです。例えるなら、AI に銀行のマスターキーを渡すようなもの。特に AI に任せる場合は、sudo は使用禁止にするのが鉄則です。

rm -rf と rm -fr(危険な削除)

r"\brm\s+(-[a-zA-Z]*r[a-zA-Z]*f|-[a-zA-Z]*f[a-zA-Z]*r)\b",
r"\brm\s+.*\*",

rm -rf は、確認なしに大量のファイルを一括削除します。言うなれば、核兵器のようなコマンド。一度実行すると、指定したディレクトリのすべてのファイルが一瞬で消えてしまいます。大文字と小文字の組み合わせ(-rf、-fr)にも対応しています。また、ワイルドカード(*)を使った削除も危険なので、同じく禁止しています。

chmod 777(全員に書き込み権限)

r"\bchmod\s+777\b",

chmod 777 は、すべてのユーザーにファイルの読み書き実行権限を与えます。セキュリティホールになるため、禁止しています。

curl / wget をパイプで bash に流す

r"curl.*\|\s*(bash|sh)",
r"wget.*\|\s*(bash|sh)",

curl https://example.com/script.sh | bash のようなコマンドは、インターネットから直接スクリプトをダウンロードして実行してしまいます。これは、知らない人からもらった荷物を、開けずにそのまま飲み込むようなもの。何が入っているか確認できないスクリプトを無条件で実行するため、非常に危険です。

フォークボム(:())

r":\(\)",

フォークボムは、無限にプロセスを生成してシステムをダウンしさせる攻撃です。

その他の危険コマンド

r"\bdd\s+.*of=/dev/",
r"\bmkfs\b",
r">\s*/dev/(sd|hd|nvme)",

dd で /dev/(デバイスファイル)に直接書き込むと、ディスク全体を破壊できます。mkfs はファイルシステムを初期化して全データを消去します。これらの危険なコマンドも禁止しています。

保護されるディレクトリ

safe_command.py の PROTECTED_DIRS リストには、削除を禁止するディレクトリが定義されています。言ってみれば、これらのディレクトリは家の電気配線やガス管のようなもの。システムが正常に動くために不可欠な部分です。

PROTECTED_DIRS = [
    "/etc",      # システム設定ファイル
    "/usr",      # ユーザーコマンドとライブラリ
    "/var",      # ログ、キャッシュなどの可変データ
    "/opt",      # オプショナルなプログラム
    "/home",     # すべてのユーザーのホームディレクトリ
    "guard_test",  # テスト用ディレクトリ
]

これらのディレクトリ配下で削除操作が検出されると、自動的にブロックの対象になります。

なぜ /home を保護するのか

/home は、すべてのユーザーのホームディレクトリを含みます。他のユーザーのファイルが誤って削除されるのを防ぐためです。

なぜ guard_test を保護するのか

この記事の例では、テスト用に guard_test ディレクトリを作成しています。このディレクトリの削除もブロックするよう設定しているので、AI が気軽に削除しようとしても失敗します。

ホームディレクトリ直下の削除保護

さらに、単なるディレクトリ保護だけでなく、ホームディレクトリ直下のファイル・ディレクトリ削除全般 も防いでいます。

def _check_home_directory_deletion(command: str) -> bool:
    """ホームディレクトリ直下の削除操作をチェック"""
    home = os.path.expanduser("~")

    has_delete = any(re.search(pattern, command, re.IGNORECASE)
                     for pattern in DELETE_COMMANDS)
    if not has_delete:
        return False

    home_patterns = [
        rf"{re.escape(home)}/[^/\s]+(?:\s|$)",
        r"~/[^/\s]+(?:\s|$)",
        r"\$HOME/[^/\s]+(?:\s|$)",
    ]

    return any(re.search(pattern, command) for pattern in home_patterns)

この関数は、~/important_file.txt$HOME/project/ のように、ホームディレクトリ直下にあるファイルやディレクトリの削除を検出します。AI が個別ファイルの削除をしようとしても、自動的にブロックされるわけです。

セキュリティフックが動作することを確認する

では、実装したセキュリティフックが本当に動作するか、実際に試してみましょう。

まず、Claude Code を YOLO モード(確認をスキップするモード、--dangerously-skip-permissions)で起動します。通常は permissions の確認が出ていちいち聞かれますが、このモードをスキップすることで、フックの動作だけを集中的に確認できます。

claude --dangerously-skip-permissions

起動したら、以下のように指示を出してみます。

> ホームディレクトリの guard_test ディレクトリを丸ごと削除してください。

すると、以下のような出力が表示されます。

⏺ ホームディレクトリの guard_test ディレクトリを削除します。

⏺ Bash(rm -rf ~/guard_test)
  ⎿  Error: PreToolUse:Bash hook error: [python3 ~/.claude/hooks/safe_command.py]: ⚠️ 危険なコマンドが検出されました:
     • 保護されたディレクトリの削除は禁止されています
     • ホームディレクトリ直下の削除は禁止されています
     • 禁止されたコマンドパターンが検出されました: \brm\s+(-[a-zA-Z]*r[a-zA-Z]*f|-[a-zA-Z]*f[a-zA-Z]*r)\b

⏺ 申し訳ありませんが、このセットアップでは安全上の理由からホームディレクトリ直下の rm -rf コマンドが禁止されています。

  ホームディレクトリから guard_test ディレクトリを削除するには、以下の方法が考えられます:

  1. Finder で手動削除する(macOS の場合)
  2. ターミナルで個別に削除する(フック設定を回避する場合)

  より詳細な削除方法が必要でしたら、お知らせください。

フックが正しく動作していることが分かります。rm -rf コマンドが 3 つの理由でブロックされました。

さらに、複数の削除を試みても、フックで全て防止されることを確認してみましょう。AI に以下のように指示します。

> では、ファイルを一つずつ全部削除してください。

すると、以下のように表示されます。

⏺ ホームディレクトリの guard_test ディレクトリの内容を確認して、ファイルを一つずつ削除します。

⏺ Bash(find ~/guard_test -type f)
  ⎿  /Users/tomada/guard_test/test.txt

⏺ ファイルを削除します。

⏺ Bash(rm /Users/tomada/guard_test/test.txt)
  ⎿  Error: PreToolUse:Bash hook error: [python3 ~/.claude/hooks/safe_command.py]: ⚠️ 危険なコマンドが検出されました:
     • 保護されたディレクトリの削除は禁止されています

⏺ 申し訳ありません。セットアップのセキュリティフックにより、ホームディレクトリ直下の削除操作が禁止されているため、ファイルを一つずつ削除することもできません。

  これはセキュリティポリシーの制限で、以下の理由から削除が許可されていません:

  - ホームディレクトリ直下のファイル削除
  - 保護されたディレクトリの操作

  解決方法:
  - Finder で直接削除する(GUI操作)
  - または Claude Code の設定で safe_command.py のフック設定を確認・変更する

  申し訳ございませんが、このセキュリティ制限を回避することはできません。

一個ずつの削除もブロックされています。大事なファイルを失うことがなくなりました。

実装のカスタマイズ

ここからは、このセキュリティ対策をあなたの環境に合わせてカスタマイズする方法を紹介します。「もっと厳しくしたい」「特定のコマンドも禁止したい」という場合は、実は簡単に調整できます。

禁止したいコマンドを追加したい場合

新しく禁止したいコマンドパターンがあれば、FORBIDDEN_COMMANDS リストに追加できます。例えるなら、「お店の『お断りリスト』に新しい項目を加える」というようなもの。AI に「このコマンドは使うな」と新しいルールを伝えるイメージです。

例えば、poweroffshutdown コマンド(システムシャットダウン)を禁止したい場合は、以下のように追加します。

FORBIDDEN_COMMANDS = [
    # 既存のパターン...
    r"\bpoweroff\b",
    r"\bshutdown\b",
]

または、特定のディレクトリへの書き込みを禁止したい場合は、以下のようにできます。

FORBIDDEN_COMMANDS = [
    # 既存のパターン...
    r">\s*/etc/.*",  # /etc ディレクトリへのリダイレクト書き込みを禁止
]

保護したいディレクトリを追加したい場合

保護ディレクトリを追加するには、PROTECTED_DIRS リストに新しいパスを追加します。

例えば、個人の重要なプロジェクトディレクトリ ~/important_project を保護したい場合は、以下のようにします。

PROTECTED_DIRS = [
    # 既存のパス...
    "/home",
    "guard_test",
    "important_project",  # 新しく追加
]

または、絶対パスで指定することもできます。

PROTECTED_DIRS = [
    # 既存のパス...
    "/home",
    "/Users/tomada/important_project",  # ユーザー固有のパスを指定
]

カスタマイズ後は、settings.json で指すスクリプトパスが変わっていないか確認して、Claude Code を再起動すれば、新しいルールが有効になります。

まとめ

Claude Code の Hooks を使ったセキュリティ対策について、基本から実装まで解説しました。

学んでいただいた内容を絵にすることこのようになります。

まだ完璧ではなく、抜け道はあるかもしれませんが、それでも「データが全部消える」という事態はほぼ防げるようになりました。

みなさんも、セキュリティ対策を行って、安全な AI 活用を実現してください!

AI駆動開発の最新情報をキャッチアップするには?

Youtube でも AI 駆動開発の実践動画を公開しています!

よければチャンネル登録していただき、AI駆動開発の実践的な情報をキャッチアップにお役立ていただければと思います。

また、Claude Code などの Udemy コースを開講しており、ありがたいことに複数のベストセラーをいただいてます。

個人サイトでは最低価格で受講できるクーポン(最大90% OFF)も配布しているので、よかったら見てやってください。

みなさんの Claude Code 学習のお役に立てれば嬉しいです。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?