Claude Code OTELユーザー特定で操作を監査ログに紐づける
Claude CodeのOTELイベントが誰の操作かをどう特定するか、認証方式ごとの識別属性の違いと自前でユーザーを紐づける方法をまとめます。
OTELイベントは誰の操作として記録されるか
Claude Codeは操作を別のサービスアカウント名義で実行しません。Bashコマンド・ファイル編集・MCP呼び出しは、そのセッションを開始した開発者本人のClaudeアカウント、またはIdP(アイデンティティプロバイダー)アイデンティティに直接紐づいて記録されます。監査担当者が「誰が何をしたか」を追うとき、まず知っておくべきはこの1点です。
各イベントには識別用の標準属性が付きます。
| 属性 | 内容 |
|---|---|
user.email | 内容OAuth認証時のメールアドレス |
user.account_uuid / user.account_id | 内容Claudeアカウントの内部ID(Anthropic管理APIの表記に対応) |
organization.id | 内容認証済みの組織UUID |
user.id | 内容初回実行時に~/.claude.jsonへ生成・保存される匿名識別子 |
session.id | 内容セッション単位の識別子 |
terminal.type | 内容検出できた場合の端末種別(iTerm.app・vscode・cursor・tmux等) |
これらはメトリクス・イベントの両方に付く標準属性です。特にuser.emailはOAuthで認証しているときだけ含まれ、組織のポリシーとして懸念があるならテレメトリバックエンド側でフィルタ・削除する運用が案内されています。terminal.typeは個人の特定には直接使いませんが、「VS Code拡張からの操作か、素のターミナルからの操作か」を切り分ける補助的な軸として、他の識別属性と組み合わせて使う価値があります。
認証方式によって埋まる属性が変わる
同じ標準属性でも、どの方式でClaude Codeが認証しているかによって埋まり方が変わります。個人を一意に特定できるかどうかで、埋まり方は次の3通りに分かれます。
| 認証方式 | 埋まる識別属性 | ユーザー特定の確度 |
|---|---|---|
| Claudeアカウント(OAuth) | 埋まる識別属性user.email / user.account_uuid / user.account_id / organization.id / user.id | ユーザー特定の確度明確に個人を特定できる |
| Claude apps gateway | 埋まる識別属性IdP由来のuser.id(subject) / user.email / user.groups / identity.source: gateway-oidc | ユーザー特定の確度明確に個人を特定できる(組織のIdPと直結) |
| 直接APIキー | 埋まる識別属性user.id(匿名)/ session.idのみ | ユーザー特定の確度個人特定には自前の紐づけが必要 |
| Amazon Bedrock / Google CloudのAgent Platform / Microsoft Foundry | 埋まる識別属性user.id(匿名)/ session.idのみ | ユーザー特定の確度個人特定には自前の紐づけが必要 |
APIキー認証やクラウドプロバイダー経由のデプロイでは、セッションにClaudeアカウントという概念自体が存在しないため、user.idとsession.idしか埋まりません。このuser.idは個人情報を含まない、Claudeアカウントとも無関係なランダム値で、~/.claude.jsonを削除すると次回実行時に無関係な新しい値へ変わります。つまり同じ開発者でも、ファイルを消せば別人として記録されます。
Claude apps gatewayのセッションだけは例外です。IdP発行トークンのsubjectをuser.idとして自動的に刻印し、user.emailにサインイン中のメールアドレス、user.groupsにIdPのグループ所属をカンマ区切りで含めます。各エクスポートにはidentity.source: gateway-oidcも付きます。このゲートウェイのアイデンティティは最後に適用されるため、OTEL_RESOURCE_ATTRIBUTES経由でuser.*やidentity.*を自前設定していても、gatewayセッションでは無視されます。
organization.idの信頼性は、ログインをどこまで制限しているかにも左右されます。管理設定のforceLoginOrgUUIDで自組織のIDを指定すると、claude.aiログインは指定した組織に属するアカウント以外での起動を拒否できます。この制限を掛けていない状態では、無関係な組織のIDがorganization.idに混ざる余地が残るため、テナント分離の軸として使う前提なら組み合わせておく価値があります。ただし検証されるのはターミナル・VS Code拡張・Agent SDKでのclaude.aiログインに限られ、次の経路は対象外です。
- クラウドプロバイダー経由のセッション(Amazon Bedrockなど): クラウド側で認証するためそもそもブロック対象外。組織側のIAMポリシーで別途絞り込む必要があります
- AnthropicプロファイルまたはFederation資格情報: これもブロックされず、しかもそのキーがどの組織に属するかを検証しません
claude setup-tokenや/install-github-app:forceLoginMethodは強制されますがforceLoginOrgUUIDは効かないため、別組織でトークンを発行できてしまいます- gatewayサインイン:
forceLoginOrgUUIDの対象外です
直接APIキー(ANTHROPIC_API_KEY / ANTHROPIC_AUTH_TOKEN / apiKeyHelper)によるセッションはむしろ逆で、forceLoginOrgUUIDを設定すると組織所属を検証できない環境資格情報とみなされ、起動そのものがブロックされます。
APIキー認証で個人特定するには自前でタグ付けする
APIキー認証やBedrock / Google CloudのAgent Platform / Microsoft Foundry経由でユーザー単位の追跡が必要な組織は、OTEL_RESOURCE_ATTRIBUTESでユーザー識別子を自分で付与します。管理設定ファイルかランチャースクリプトでユーザーごとに設定する運用です。
export OTEL_RESOURCE_ATTRIBUTES="enduser.id=jdoe@example.com,enduser.directory_id=S-1-5-21-..."OTEL_RESOURCE_ATTRIBUTESのキーはOTEL_METRICS_INCLUDE_RESOURCE_ATTRIBUTES(既定true)が有効な限りメトリクスの属性にも乗ります。部署名やチームIDのような組織内の分類子を持たせたい場合も同じ変数を使い、department=platform-teamのようなキーを追加すれば、ユーザー識別子と同じ属性としてイベント・メトリクス双方に反映されます。
ただし公式ドキュメントは、カスタムキー1つ1つが全メトリクスのラベルになるため、高カーディナリティの値(ユーザーIDなど利用者数だけ値が増える属性)を含めるとストレージコストが増える、と注意を促しています。ユーザー識別子はメトリクス側では不要でイベント側だけで足りるという場合は、OTEL_METRICS_INCLUDE_RESOURCE_ATTRIBUTES=falseにすればリソースブロックのメタデータには残しつつメトリクスのラベルからは外せます。組織全体への配信方法はClaude Code組織管理ガイドを参照してください。
どのイベントに個人の操作履歴が乗るか
ユーザー識別属性は全イベント共通ですが、何をしたかの詳細度は別の設定に依存します。MCP呼び出し・Bashコマンド・ファイル編集の内容まで監査したいならOTEL_LOG_TOOL_DETAILS=1が必須です。フラグを立てないまま運用していると、「誰が」までは常に追えるのに「何を」の部分だけがユーザー設定のMCPサーバーで欠けている、という気づきにくいギャップが生まれます。
1つのプロンプトが複数のAPIリクエストやツール実行を引き起こしたとき、それらをまとめて追いたければprompt.idを軸にします。UUIDv4でそのプロンプト処理中に発生した全イベントを紐づけます。さらにmessage.uuidを見れば、セッションのトランスクリプト(~/.claude/projects/*/*.jsonl)上のどのメッセージに対応するイベントかまで特定できます。
APIリクエスト単位で追いたい場合はclient_request_idも使えます。api_request・api_errorに共通で付くクライアント生成のUUIDで、タイムアウトのようにサーバー側のrequest_idが返らなかった失敗でも、どのリクエストがどのユーザーのどの操作に由来するかを追跡できます。
user_promptイベントのcommand_name / command_sourceも見落とされがちな識別情報です。ユーザーがどのスラッシュコマンドを実行したかを記録し、compactやdebugのような組み込みコマンドはそのまま記録されますが、カスタムコマンド・プラグインコマンド・MCPコマンドの名前はOTEL_LOG_TOOL_DETAILS=1を設定しない限りcustomまたはmcpに丸められます。「誰が」だけでなく「どの操作を呼んだか」まで監査したいなら、このフラグの有無が結果を左右します。
誰の判断で許可・拒否されたかをsource属性で見分ける
同じユーザーの操作でも、ツール実行の許可・拒否が本人の判断によるものか、設定やhookによる自動判断かは監査上まったく別の意味を持ちます。tool_decisionイベントのsource属性がこれを区別します。
sourceの値 | 意味 |
|---|---|
config | 意味プロジェクト設定・ユーザー設定のallow/denyルール、--allowedTools等のフラグ、現在の権限モード、同一セッション内の既存の許可のいずれかで自動判断。どのルールが一致したかまでは記録されない |
hook | 意味PreToolUseまたはPermissionRequestフックが判断を返した |
user_permanent | 意味ユーザーが「今後も確認しない」を選び、個人設定にallowルールが保存された瞬間の操作。以降の同じ呼び出しはconfigとして記録される |
user_temporary | 意味ユーザーがその場限りの承認を選んだ、またはセッション内だけ有効な許可を選んだ瞬間の操作 |
user_abort | 意味ユーザーが許可プロンプトに応答せず中断した(拒否として扱われる) |
user_reject | 意味ユーザーが明示的に「いいえ」を選んだ |
ここで重要なのは、user_permanentやuser_temporaryが記録されるのはその選択をした瞬間だけという点です。同じルールに後から一致した呼び出しはconfigとして記録されます。つまり「この操作はconfigだから自動承認だ」と早合点すると、実は数分前に同じユーザーが明示的に許可した結果を見落とすことがあります。ある操作が本当に人手を介さず通ったのかを追うなら、そのセッションの最初のuser_permanent / user_temporaryイベントまで遡って確認する必要があります。
コスト・利用量も同じ識別属性で個人単位に分解できる
ユーザー識別はイベントだけの話ではありません。claude_code.cost.usageやclaude_code.token.usageのようなメトリクスにも同じ標準属性(user.email・user.account_uuid・session.id)が付くため、「誰が」「どのモデルで」「どのスキルやサブエージェント経由で」コストを使ったかを分解できます。
コストメトリクスにはmodel・skill.name・plugin.name・agent.name・mcp_server.nameという属性も付きます。特定の開発者だけが突出したコストを出しているのか、特定のスキルやMCPサーバーが原因なのかを、ユーザー識別属性と掛け合わせて切り分けられる設計です。ある個人のコストが急増したとき、単に「使いすぎ」なのか、意図しないループを起こすカスタムスキルを実行し続けているのかを、user.account_uuidで絞り込んだ上でskill.name別に内訳を見れば判別しやすくなります。ただし同じ内訳分解でもagent.name(ユーザー定義エージェント名)とmcp_server.name(ユーザー設定サーバー名)はcustomに丸められる仕様のため実名までは追えず、skill.nameはユーザー定義でも実名のまま残るという差があります。
ただしuser.account_uuidとuser.account_idはOTEL_METRICS_INCLUDE_ACCOUNT_UUID(既定true)がオンの場合のみメトリクスに付きます。個人単位のコスト分解を今後も使う前提なら、この変数を誤ってfalseにしないよう管理設定側で明示しておくと安全です。
OTELユーザー特定は「誰が」であって「何を言ったか」ではない
ユーザーの身元を特定できることと、プロンプトや応答の中身が記録されることは別の設計です。既定では、プロンプト本文もClaudeの応答テキストも記録されず、長さだけが記録されます。中身を含めるにはOTEL_LOG_USER_PROMPTS=1とOTEL_LOG_ASSISTANT_RESPONSES=1をそれぞれ明示する必要があり、後者を未設定のままにすると前者の値にフォールバックする仕様です。ツールの入力引数も既定では記録されず、OTEL_LOG_TOOL_DETAILS=1で初めてtool_parameters・tool_inputに乗ります。
識別属性だけでなく、実際にどのイベントで何が追えるかを質問ベースで一覧したい場合はClaude Codeセキュリティ監査とOTELイベントの対応表も合わせて参照してください。
これは監査要件と衝突しません。「誰が」「いつ」「どのツールを」「承認したか拒否したか」までは既定の識別属性だけで追え、そこに「具体的に何を入力・出力したか」を足すかどうかは別レバーとして分離されています。組織のプライバシーポリシーに応じて、身元の特定だけを既定運用にし、内容の記録は必要な調査期間だけOTEL_LOG_TOOL_DETAILS等を一時的に有効化する、という段階的な運用が成立する設計です。監査ログ基盤へのSIEM連携そのものの設定はClaude Code SIEM連携でOTELイベントを監査基盤に送るにまとめています。
まとめ
Claudeアカウントで認証しているセッションなら、user.emailとuser.account_uuidで個人を明確に特定できます。APIキーやクラウドプロバイダー経由のデプロイではuser.idが匿名の乱数にとどまるため、個人単位の追跡が必要な組織はOTEL_RESOURCE_ATTRIBUTESで自前のユーザー識別子を付与します。Claude apps gatewayはIdPアイデンティティを自動で刻印し、この自前設定より優先されます。「誰が」を特定する識別属性と、「何を」記録するかを決めるOTEL_LOG_*系の各フラグは独立したレバーなので、監査要件に応じて別々に設計できます。
よくある質問
user.idだけでユーザーを一意に特定できますか
Claudeアカウントを使わないセッションでは、user.idは個人情報を含まないランダム値です。~/.claude.jsonを削除すると次回実行時に無関係な新しい値になるため、これ単体では長期的な個人特定の軸として使えません。個人単位の追跡が必要ならOTEL_RESOURCE_ATTRIBUTESで自前の識別子を付与します。
複数のツール呼び出しをまとめて1つのプロンプトの操作として追うには
prompt.id属性を使います。1つのユーザープロンプトの処理中に発生した全イベントに同じUUIDv4が付くため、これでグルーピングできます。
user.emailはどんな場合に記録されますか
OAuthでClaudeアカウントに認証しているときだけ含まれます。直接APIキーやクラウドプロバイダー経由の認証では含まれません。
プロンプトの中身まで記録せずに、誰が何回操作したかだけを追う設定はできますか
できます。OTEL_LOG_USER_PROMPTS・OTEL_LOG_ASSISTANT_RESPONSES・OTEL_LOG_TOOL_DETAILSをすべて未設定(既定の無効)のままにすれば、ユーザー識別属性とイベントの種類・成否だけが記録され、プロンプトや応答、ツール引数の中身は記録されません。
Claude apps gateway経由のセッションでOTEL_RESOURCE_ATTRIBUTESにユーザー情報を設定したらどうなりますか
無視されます。gatewayセッションではIdP由来のアイデンティティが最後に適用される仕様のため、OTEL_RESOURCE_ATTRIBUTESで設定したuser.*やidentity.*のキーより優先されます。
個人の識別だけでなく、判断のロジックまで監査で追うにはどうすればいいですか
識別属性だけでは「誰が・いつ・何を承認したか」までしか分かりません。config判定の内訳が追えないといった判断ロジック側の限界も含め、どのイベントで何を追えるかはClaude Codeセキュリティ監査とOTELイベントの対応表に集約してあります。判断ロジックまで踏み込んだ監査を組むときはそちらを参照してください。