Claude Codeセキュリティ監査とOTELイベントの対応表
セキュリティ監査でよく出る質問と、それに答えるClaude CodeのOTELイベント・属性を1対1で引ける対応表にまとめます。
このTipsでできること
セキュリティ監査で「この質問にはどのイベントを見ればいいか」を都度ドキュメントから探す代わりに、質問からイベント名・属性へ一発で引ける対応表をまとめます。Claude Codeはイベントの生ストリームを出すだけで、異常検知やベースライン化、セッションをまたいだ相関分析、アラートの発報はSIEMや可観測性バックエンド側の責務です。この記事は「どのデータがどこにあるか」の地図に徹します。
監査の質問からイベントを引く
対応表は「知りたいこと」から「イベント名」「見るべき属性」「追加で必要な設定」の3つを1行で引ける形にしています。「必要な設定」列にOTEL_LOG_TOOL_DETAILS=1と書かれた行は、そのフラグを有効にしないと該当の属性が伏せられたまま記録されます。「標準で取得可」の行は追加設定なしで取得できます。
| 知りたいこと | イベント | 主な属性 | 必要な設定 |
|---|---|---|---|
| コマンドが許可・拒否されたか、誰の判断か | イベントtool_decision | 主な属性decision・source・tool_name | 必要な設定標準で取得可(引数はOTEL_LOG_TOOL_DETAILS=1) |
| 実行されたコマンド・触れたファイル | イベントtool_result(実行時)/ tool_decision(拒否時) | 主な属性tool_parameters・tool_input | 必要な設定OTEL_LOG_TOOL_DETAILS=1 |
権限モードが昇格したか(auto→bypassPermissions等) | イベントpermission_mode_changed | 主な属性from_mode・to_mode・trigger | 必要な設定標準で取得可 |
| ポリシーhookがアクションをブロックしたか | イベントhook_execution_complete | 主な属性hook_event・num_blocking | 必要な設定標準で取得可 |
| ログイン・ログアウト・認証失敗 | イベントauth | 主な属性action・success・error_category | 必要な設定標準で取得可 |
| MCPサーバーの接続・切断・失敗 | イベントmcp_server_connection | 主な属性status・server_name・is_plugin・error_code | 必要な設定server_nameはOTEL_LOG_TOOL_DETAILS=1 |
| プラグインがインストールされたか、出所は | イベントplugin_installed | 主な属性plugin.name・marketplace.name・marketplace.is_official | 必要な設定サードパーティ名はOTEL_LOG_TOOL_DETAILS=1 |
| フリート全体で有効なプラグインの棚卸し | イベントplugin_loaded | 主な属性plugin.scope・enabled_via・has_hooks・has_mcp | 必要な設定標準で取得可(名前の一部は要フラグ) |
| フリート全体で設定されているhookの棚卸し | イベントhook_registered | 主な属性hook_event・hook_type・hook_source・safe_mode | 必要な設定標準で取得可 |
| APIリトライが枯渇したか(異常・障害の兆候) | イベントapi_retries_exhausted | 主な属性total_attempts・status_code | 必要な設定標準で取得可 |
| どのスキルがいつ起動されたか | イベントskill_activated | 主な属性skill.name・invocation_trigger・skill.source | 必要な設定カスタムスキル名はOTEL_LOG_TOOL_DETAILS=1 |
| サブエージェント実行の集計 | イベントsubagent_completed | 主な属性agent_type・total_tool_uses・duration_ms | 必要な設定カスタムエージェント名はOTEL_LOG_TOOL_DETAILS=1 |
| 予期しない内部エラーの発生 | イベントinternal_error | 主な属性error_name・error_code | 必要な設定標準で取得可(メッセージ・スタックトレースは含まれない) |
| セッションデータが保持期間どおり削除されたか | イベントretention_sweep | 主な属性result・transcripts_deleted・period_days | 必要な設定標準で取得可 |
公式のイベント一覧には無い、プラグイン・hookの棚卸し、リトライ枯渇、保持期間の消込みの行も含めています。hookそのものの設定方法や種類はClaude Code Hooks完全ガイドにまとめています。ユーザーを誰の操作かに紐づける属性の詳細はClaude Code OTELユーザー特定、実際にこれらのイベントをSIEMへ届ける設定はClaude Code SIEM連携にまとめています。
OTEL_LOG_TOOL_DETAILSが要る行と要らない行を見分ける
対応表の「必要な設定」列がすべての行で同じではない点に注意します。decisionやstatusのような判断結果そのものは標準の識別属性だけで取得できます。一方、tool_parametersやtool_inputのような何を実行したかの中身はOTEL_LOG_TOOL_DETAILS=1を設定して初めて乗ります。
MCPの監査で特に効いてくるのがこの違いです。フラグなしの既定状態でも、mcp_server_connectionイベントで接続の成否(status)は追えますが、server_nameとエラーメッセージは伏せられます。それでもis_pluginとplugin_id_hashは残るため、「プラグイン経由のMCPサーバーで接続障害が起きたか」自体は判別できます。プラグイン名についても段差があり、公式マーケットプレイス由来のプラグインはplugin.nameが常に実名で記録される一方、サードパーティのプラグイン名は"third-party"という文字列に丸められます。ここまではフラグなしで得られる情報で、公式・非公式の切り分けだけならフラグなしでも可能です。
OTEL_LOG_TOOL_DETAILS=1を設定すると、この段差が一気に埋まります。server_nameとエラーメッセージが記録され、tool_result / tool_decisionイベント側でもMCPサーバー名・ツール名・呼び出し引数(tool_parameters / tool_input)まで乗るようになります。「どのMCPサーバーの、どのツールが、どんな引数で呼ばれたか」まで踏み込んだ監査には、フラグを有効にする必要があります。
plugin_installedとplugin_loadedは別の質問に答える
対応表には似た名前のイベントが2つあります。plugin_installedはインストールという操作そのものを記録し、install.triggerでCLIコマンド経由かインタラクティブな/pluginUI経由かまで分かります。一方plugin_loadedはセッション開始のたびに、その時点で有効になっているプラグイン全部を1件ずつ記録します。
「いつ・誰が・どこからインストールしたか」を追うならplugin_installed、「今フリート全体でどのプラグインが実際に動いているか」を棚卸しするならplugin_loadedです。後者にはenabled_viaという属性があり、組織ポリシーで強制インストールされたのか(org-policy・admin-install)、開発者が個別に入れたのか(user-install)まで区別できます。監査で「管理者が把握していないプラグインが動いていないか」を確認するときは、enabled_viaがuser-installのもの、かつplugin.scopeがcommunityやuser-localのものを重点的に洗い出す、という絞り込みが効きます。
対応表だけでは読み取れない限界
生イベントだけでは答えられない質問もあります。たとえばhook_execution_completeのnum_blockingは、その回に何個のhookがブロック判定を返したかは分かりますが、複数のhookが登録されている場合にどのhookがブロックしたかまではこのイベント単体では特定できません。hook_name(マッチャー込みのフック名)で対象範囲を絞り込むところまでが限界で、それ以上の切り分けにはhook自体のログを突き合わせる必要があります。
hook_eventの値によって発生頻度の意味も変わります。PreToolUseはツール呼び出しのたびに、権限確認の要否にかかわらず毎回走るため、このhook_eventのhook_execution_completeは極めて高頻度に発生します。一方PermissionRequestは実際に許可プロンプトが必要になったとき、またはプロンプトを出せない状況で自動拒否するときにしか走りません。同じnum_blockingという数値でも、PreToolUse側の集計とPermissionRequest側の集計では意味する頻度の母数が違う点を踏まえて解釈する必要があります。
同様にtool_decisionのsourceが"config"のとき、プロジェクト設定・ユーザー設定・エンタープライズのポリシー・CLIフラグ・現在の権限モード・セッション内の既存許可のどれが一致したのかは、イベント単体では区別されません。どのルールが実際に効いたかまで追うには、対象マシンのsettings.json階層を別途確認する必要があります。OTELイベントは「何が起きたか」を高い網羅性で記録しますが、「なぜそう判断されたか」の内部ロジックまでは踏み込まない設計だと理解しておくと、対応表を過信せずに済みます。
イベントを横断して1つの操作を追う
1件のセキュリティインシデントを調べるとき、複数のイベントを同じ操作に属するものとしてまとめる必要が出てきます。使える相関キーは3つです。
prompt.id: 1つのユーザープロンプトの処理中に発生した全イベントに共通で付くUUID。あるコマンド実行の前後で何が起きたかを時系列で追うときの軸になりますsession.id: セッション単位。ログイン(auth)からログアウトまでの一連の操作をまとめて見るときに使いますtool_use_id: 1回のツール呼び出しに対してtool_resultとtool_decisionの両方に共通で付く識別子。フックが捕捉したデータとOTelイベントを突き合わせるときの軸です
たとえば「あるBashコマンドが拒否されたあと、権限モードがエスカレーションされて再実行されたか」を追うには、tool_decision(1回目、reject)→permission_mode_changed→tool_decision(2回目、accept)の順でprompt.idかsession.idをキーに並べます。
シナリオで対応表を通しで使う
「危険なコマンドが実行された疑いがある」という典型的な調査を、対応表の行を実際につないで追ってみます。
まずsession.idで対象セッションを絞り込みます。次にtool_decisionイベントを時系列で並べ、tool_nameがBashのものに絞ります。decisionがacceptでsourceがhookなら、ポリシーhookが自動で許可したことになるので、そのhookの設定意図が正しかったかを確認します。sourceがuser_temporaryやuser_permanentなら、開発者本人がその場で許可したことになります。
危険なコマンドがブロックされていたはずなのに実行されていた場合は、hook_execution_completeのnum_blockingが0だったか、そもそも該当のhook_eventに一致するhookがhook_registeredに存在するかを確認します。hookが設定されていない、あるいはマッチャーの範囲外だったというケースは珍しくありません。
このコマンドの実行が権限モードのエスカレーション直後だった場合は、permission_mode_changedを遡ってtriggerを見ます。shift_tabならユーザーが手動で切り替えた操作、auto_gate_deniedならautoモードのゲートが拒否した結果として別モードへ落ちた操作です。最後にtool_resultで実際に何が実行されたか(OTEL_LOG_TOOL_DETAILS=1が設定されていればtool_parametersのfull_command)を確認すれば、承認の経緯から実行内容までを1本の時系列として再構成できます。
サブエージェントが実行した操作も同じ経路で追える
サブエージェントの実行を監査するときは、見る軸が2つに分かれる点に注意します。ツール呼び出しの中身を追う軸と、コスト・トークンを集計する軸は別のイベントです。
「あるサブエージェントが何をしたか」の概要をつかむにはsubagent_completedイベントのagent_type・total_tool_uses・duration_msを見ます。個々のツール呼び出しまで遡るには、起動元と同じsession.idを持つtool_decision / tool_resultイベントを時系列で並べ、OTEL_LOG_TOOL_DETAILS=1を設定していればtool_parameters内のsubagent_typeでAgent tool(または旧Task tool)経由の呼び出しを絞り込みます。一方、query_source("main" / "subagent" / "auxiliary")はコスト・トークンのメトリクス側の軸で、サブエージェント分のコストやトークンをロールアップしたいときはトークンカウンター・コストカウンターをquery_sourceが"subagent"のものに絞ります。両者は別の質問に答える別の軸なので混同しないようにします。バックグラウンドで動くサブエージェント(is_asyncがtrue)もsubagent_completedに同じ形で記録されるため、非同期実行だからといって監査の対象から漏れることはありません。
まとめ
セキュリティ監査で聞かれる質問の多くは、対応表の行を1つ引くだけでどのイベント・どの属性を見ればいいかが分かります。判断結果(許可/拒否・接続成否・モード変更)は標準属性だけで追え、コマンドの中身やMCPの引数まで踏み込むにはOTEL_LOG_TOOL_DETAILS=1が必要という区別さえ押さえておけば、監査要件に対してどの設定が要るかを迷わず判断できます。
よくある質問
この対応表の行はどれくらいの頻度で増えますか
Claude Codeのバージョンアップに伴って新しいイベント・属性が追加されることがあります。組織固有の監査要件に対応するイベントが見当たらない場合は、公式のイベント一覧を都度確認するのが確実です。
異常検知のルールもこの記事の対応表だけで組めますか
組めません。Claude Codeが出すのはイベントの生ストリームだけで、ベースライン化・異常検知・セッションをまたいだ相関分析・アラートの発報はSIEMや可観測性バックエンド側で設計する必要があります。この対応表は「どのデータがどこにあるか」までを担います。
プラグイン経由のhookとユーザー定義のhookを対応表で区別できますか
できます。hook_registeredのhook_sourceがpluginHookかどうかで判別でき、pluginHookの場合はplugin.name(サードパーティはOTEL_LOG_TOOL_DETAILS=1が必要)でどのプラグインが提供したhookかまで特定できます。
tool_decisionとtool_resultはどう使い分けますか
tool_decisionは許可・拒否の判断そのもの、tool_resultはツールが実際に実行を完了したときにだけ発行されます。拒否されたツール呼び出しは実行されないためtool_resultは出ません。「拒否された呼び出しの中身」を見たいときはtool_decision側のtool_parametersを見ます。
--safe-modeで起動したセッションかどうかも対応表で追えますか
追えます。hook_registered / hook_execution_start / hook_execution_completeのいずれにもsafe_mode属性があり、そのセッションが--safe-modeで起動されていたかを判別できます。安全モードのセッションで期待どおりhookが動いていなかった場合の切り分けに使えます。