0
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?

自作Hooksで防ぐ、Vibe Coding時代のシークレット漏洩 ― お願いではなく物理ガードレールを

0
Posted at

Claude CodeやCursorのようなAIコーディングツールが当たり前になり、指示を出せばコードが数十行単位で返ってくる時代になりました。便利になった一方で、「AIに指示さえ出せば動くものができる」という開発スタイル――俗にVibe Coding(バイブコーディング。AIが生成したコードを人間が細部まで理解しないまま受け入れて開発を進めるスタイル)――が広がるにつれ、静かに増えているのがシークレットの漏洩事故です。

APIキー、DBのパスワード、認証トークン。こうした「シークレット」がAIエージェントの作業ログやコミット、時には出力そのものに紛れ込み、外部に漏れてしまう。CLAUDE.mdや指示書に「シークレットを扱わないでください」と書いておくだけでは、この事故を防げません。本稿では、なぜ「お願い」では不十分なのか、そしてClaude CodeのHooks機能を使った自作の物理ガードレールでどう対処できるのかを、具体的なコードとともに解説します。

この記事で分かること

  • なぜCLAUDE.mdやSkillsに書いたルールだけでは、AIエージェントの事故を防げないのか
  • Vibe Coding時代に実際に報告されているシークレット漏洩・悪用の手口
  • Claude CodeのHooks機能の仕組みと、シークレット漏洩を防ぐ自作Hookの実装例
  • Hooksにも限界があること、そして限界を踏まえた現実的な防御の組み方

前提知識:Hooksとシークレットとは何か

まず言葉を整理します。

シークレットとは、AWSのアクセスキーやデータベースのパスワード、Slackのトークンのように、外部に漏れると不正利用につながる機密情報の総称です。多くの場合、開発中は.envファイルや環境変数に置かれています。

Hooksは、Claude Codeが特定の動作(ファイル編集、コマンド実行、応答の完了など)をするたびに、あらかじめ登録しておいた自分のスクリプトを自動実行できる仕組みです。~/.claude/settings.jsonに設定を書くと、Claude Codeがツールを呼び出す直前・直後や、セッションの節目でスクリプトが起動します。

ここで役立つたとえ話があります。CLAUDE.mdに「鍵をかけてから外出してください」と書いておくのは、玄関に貼り紙をするようなものです。読む人(この場合はAI)がその瞬間に貼り紙を見落とせば、意味を持ちません。一方Hooksは、鍵をかけ忘れて外に出ようとした瞬間に自動でロックがかかる、後付けの電子錠に近い存在です。「言葉で頼む」のか「物理的に止める」のか、この違いが本稿の核心です。

なぜ「ルールを書くだけ」では足りないのか

AIエージェントの運用ノウハウを共有するある技術ブログでは、「Hooksでできることをスキル(指示文書)に書いてしまう」失敗パターンが紹介されています。「ファイルを編集するたびにエラーを検出してほしい」「コミットのたびにメッセージ品質をチェックしてほしい」――こうした「〇〇のたびに△△してほしい」という要求を指示文書に書いても、AIは「やっておきますね」と応答するだけで、実際には毎回自動実行されません。指示文書はあくまで「知識・方針をAIに注入する場所」であり、確実な自動実行を保証する仕組みではないからです。

これはシークレット管理にもそのまま当てはまります。「APIキーをコミットしないでください」とCLAUDE.mdに書いても、それは行動を保証するものではなく、AIへの「お願い」に過ぎません。長いセッションの後半になるほど指示が読み飛ばされやすくなる、CLAUDE.mdが肥大化して重要なルールが埋もれる、といった現象も実際に報告されています。

一方でVibe Coding自体は悪ではありません。危険なのは「セキュリティ意識なしにVibe Codingを行うこと」です。AIが生成したコードにdebug=Trueやワイルドカードの権限設定がそのまま残っていたり、サンプルコードとしてハードコードされた認証情報がそのままプロダクションに紛れ込んだりする――こうした事故は、コードを人間が細部まで読まなくなったことの直接的な帰結です。

Vibe Coding時代に実際に報告されている脅威

「そんな極端な話は自分には起きない」と思う方のために、2025年から2026年にかけて複数のセキュリティ専門メディアが報じた事例を、報道された数値とともに整理します。以下はいずれも各報道・調査時点の公開情報に基づく推定値であり、実際の被害件数そのものではない点に留意してください。

時期 事案 概要
2025年1月 あるAIサービスのDB露出 データベースが認証なしで公開状態になっており、チャット履歴やAPIキーを含む100万行超のログが露出したと報じられた
2025年4月 Slopsquatting(スロップスクワッティング) 研究チームが16のLLMに対し57.6万件のコード生成プロンプトを実行したところ、推奨されたパッケージの約19.7%が実在しなかった。攻撃者はこの「AIが幻視した架空パッケージ名」を先取り登録し、マルウェアを仕込む
2026年1月 偽AIブラウザ拡張機能 「AIがメールを書いてくれる」等を謳う悪意ある拡張機能が公式ストアに大量出現し、合計90万ダウンロード規模に達したと報じられた。会話内容や閲覧履歴が外部送信されていた
2026年2月 ある製品群への大規模侵害 高度な専門性を持たない攻撃者が、偵察からデータ窃取までの全工程でAIを活用し、55カ国以上・600台以上のデバイスを侵害したと報じられた。使われたのはゼロデイ脆弱性ではなく、公開された管理ポートと弱いパスワードだった
2026年3月 偽インストールサイトによるマルウェア配布 AIコーディングツールの公式インストールページを模した偽サイトが検索広告経由で表示され、ターミナルに貼り付けさせたコマンドでマルウェアを配布したと報じられた
2026年3月 悪意あるRustクレート .envファイルの中身をビルド時に外部送信しようとするコードを含むクレートが複数報告された

この一覧から読み取れる共通点は、「開発者のローカル環境にあるシークレットそのもの」が攻撃者にとって最も価値の高いターゲットになっているということです。本番デプロイ権限、クラウドの認証情報、SSH鍵――AIエージェントに広い権限を渡して作業させる時代だからこそ、シークレットの管理は「開発者個人の注意力」任せにできません。

Hooksの4つのパターンを整理する

Claude CodeのHooksは、発火するタイミングによっていくつかの型に分類できます。まず全体像を押さえておきましょう。

パターン 発火タイミング 目的 具体例
Safety Gates PreToolUse(ツール実行の直前) 危険な操作そのものをブロックする .envファイルへの書き込みを検知して止める
Quality Loops PostToolUse(ツール実行の直後) 即座にフィードバックし、必要なら修正を促す 生成されたコードをリンティングし、結果をAIに返す
Completion Gates Stop(応答の完了時) タスクが本当に完了したかを検証する テストが通るまで完了扱いにしない
Observability 全イベント共通 何が起きたかを記録し、後から監査できるようにする どのファイルが変更されたかをログに残す

シークレット漏洩を防ぐという目的に対しては、Safety Gates(PreToolUse)が本命です。実行される前に止めれば、そもそも漏洩は発生しません。Quality LoopsやObservabilityは「起きてしまった後に気づく」ための仕組みであり、防御としては一段弱くなります。

自作Hookを実装する:シークレット検知の最小構成

ここから実際にコードを書きます。目的は、AIエージェントがファイルを書き込もうとした瞬間に、その内容にシークレットらしき文字列(AWSのアクセスキー、汎用的なAPIキー・トークンのパターン、.envファイルそのものへの書き込みなど)が含まれていないかを検査し、含まれていれば処理を止めることです。

まず全体の流れを図で示します。

次に、実際のHookスクリプト例です。Claude CodeのHookには、実行対象のツール名や入力内容がJSON形式で標準入力(stdin)から渡されます。スクリプト側でその中身を検査し、危険と判断したら終了コード2を返すことでツールの実行そのものをブロックできます。

#!/bin/bash --norc
# secret-guard.sh — PreToolUseで発火し、書き込み内容にシークレットらしき文字列がないか検査する

INPUT="$(cat)"

TOOL_NAME="$(echo "$INPUT" | jq -r '.tool_name // empty')"
FILE_PATH="$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')"
CONTENT="$(echo "$INPUT" | jq -r '.tool_input.content // .tool_input.new_string // empty')"

# 対象は書き込み系ツールのみ
case "$TOOL_NAME" in
  Write|Edit) ;;
  *) exit 0 ;;
esac

# .env ファイル本体への書き込みはまず疑う
if [[ "$FILE_PATH" =~ \.env($|\.[a-zA-Z]+$) ]]; then
  echo "警告: .env系ファイルへの書き込みが検出されました($FILE_PATH)" >&2
  exit 2
fi

# 代表的なシークレットのパターンをチェック
if echo "$CONTENT" | grep -qE 'AKIA[0-9A-Z]{16}'; then
  echo "警告: AWSアクセスキーらしき文字列が検出されました" >&2
  exit 2
fi

if echo "$CONTENT" | grep -qiE '(api[_-]?key|secret|password|token)["'\'']?\s*[:=]\s*["'\''][a-zA-Z0-9_\-]{16,}["'\'']'; then
  echo "警告: シークレットらしきキー・バリューが検出されました" >&2
  exit 2
fi

exit 0

これを~/.claude/settings.jsonに登録します。

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "cat | bash /path/to/hooks/secret-guard.sh"
          }
        ]
      }
    ]
  }
}

ここで押さえておきたい実装上の注意点が2つあります。

1つ目は、INPUT="$(cat)"は必ずスクリプトの冒頭で実行することです。標準入力は一度しか読み取れないため、後から読み直そうとしても空になります。

2つ目は、.bashrcなどの読み込みでJSON出力が壊れることがあるため、#!/bin/bash --norcでシェル起動時の余計な読み込みをスキップしておくことです。Hook側の出力に余計な文字列が混じると、Claude Code側でJSONとして解釈できずエラーになります。

この最小構成でも、「AIエージェントが誤って.envの中身をコピーしたコードを書こうとした」「サンプルコードとしてAPIキーをハードコードしてしまった」といった典型的な事故は、実行される前に機械的に止められます。

よくある落とし穴と、上級者が向き合うべきトレードオフ

ここまで読むと「Hooksを入れれば万事解決」と思うかもしれませんが、そう単純ではありません。実際にClaude Codeを複数リポジトリで運用しているエンジニアの報告からは、いくつもの現実的な落とし穴が見えてきます。

過剰ブロックによる作業の停滞。 Hookを増やしすぎると、正当な操作まで巻き添えで止まるようになります。実際に、pushを検査するHookを粗く書いたために、想定していなかった命名のブランチへの正当なpushまで止めてしまい、後から調整に追われたという報告があります。検査対象を絞り込むこと、そして定期的に見直すことが運用コストとして必要になります。

Hookはシェル構造を完全には解釈できない。 上のスクリプト例のような文字列パターンマッチは、変数を経由して組み立てられたコマンドや、別名(エイリアス)を使った迂回には対応できません。権限ルール側でコマンドの引数を絞る形にしても、表記の揺れによってすり抜けが起こりえます。Hookは「事故の確率を下げる装置」であって、「絶対に防ぐ壁」ではないという前提を持つ必要があります。

運用ルールは人間が破れる。 これはHooks特有の話ではありませんが、CLAUDE.mdのような文書ベースのルールは、そもそも人間にとっても破りやすいものです。だからこそ、運用ルール(文書)・権限ルール(settings.jsonのpermissions)・Hooks(物理ゲート)という三層を重ねて考えるのが現実的です。どれか一つに頼るのではなく、層を重ねることで、単一の防御が抜けても他の層でカバーできる状態を作ります。

「権限管理やコードレビューの方が本質的で、Hooksは補助にすぎないのでは」という考え方には一理あります。実際、Hookだけで完結させようとすると、上述のようにすり抜けや過剰ブロックの問題が必ず出てきます。それでも筆者がHooksを最初の一枚に推す理由は、権限管理やレビューは「人間が気づけるかどうか」に依存する一方、Hooksは条件に一致した瞬間に機械的に止まるという性質にあります。人間のレビューが漏れても、Hookが最後の砦として残る構成にしておく方が、事故の再発を防ぎやすいというのが実務上の判断です。

まとめ:今日やること

AIエージェントに任せる範囲が広がるほど、「言葉で頼む」対策と「物理的に止める」対策の差は大きくなります。CLAUDE.mdやSkillsに書いたルールは、AIへの意思表示としては意味がありますが、実行を保証するものではありません。Vibe Coding時代に報告されているシークレット漏洩の多くは、まさにこの「保証のなさ」につけ込む形で起きています。

今日やることを1つだけ挙げるなら、自分のプロジェクトの~/.claude/settings.jsonに、.envファイルへの書き込みを検知してブロックするだけの最小のPreToolUse Hookを1つ追加してみることです。本稿のスクリプト例をそのまま使ってもかまいません。まずは小さく1つ動かしてみることで、「ルールを書く」と「実行を止める」の違いを体感できるはずです。


こちらもよく読まれています

0
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
0
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?