前回の記事では、Prometheusで異常な区間を見つけ、TempoのTraceでリクエストパスを絞り込み、Lokiで同じTraceIdの詳細な根拠を確認しました。しかし、痕跡を見つけたからといってインシデント対応が完了するわけではありません。調査結果を保存し、影響範囲を定め、危険な経路を遮断し、サービスを安全に復旧させる必要があります。
この記事では、「入力Guardrailがブロックを記録したにもかかわらず、その後にTool呼び出しが実行された」という仮想的な事件を最初から最後まで処理します。目標は、攻撃文字列を再実行することではありません。すでに収集されたLog・Trace・Metricとデプロイ情報を利用して事件を再構成し、再現可能な証拠の束と対応記録を作成する防御演習です。

インシデント調査はログ検索以上の大仕事である
NIST SP 800-61 Rev.3は、インシデント対応を個別の単発作業ではなく、組織のサイバーセキュリティリスク管理活動全体に統合するよう説明しています。準備ができていれば、検出・対応・復旧が迅速になり、復旧から得られた教訓が、保護と検出能力を再び改善します。
LLMサービスでは、この循環が特に重要です。モデル呼び出しだけを調査するだけでは不十分です。ユーザー認証、テナント境界、システムプロンプトのバージョン、RAG検索結果、Guardrailの決定、Toolの権限、ダウンストリームの変更、出力処理までを一つの事件としてまとめる必要があります。
준비 → 탐지·분석 → 범위 확정 → 증거 보존
↑ ↓
개선 ← 사후 분석 ← 복구·검증 ← 차단·근절
まず、役割と決定権を定める
観測ツールは事実を示しますが、対応の決定を代行するものではありません。インシデントコマンダーは優先順位と意思決定を調整し、調査担当者は証拠とタイムラインを作成します。サービス担当者は機能制限と復旧を実行し、IAM担当者はアカウント・トークン・権限を点検します。個人情報や規制対象データが関連する場合は、法務・個人情報担当者が保存期間と通知要件を判断します。
| 役割 | 主な責任 | 単独で行うべきではないこと |
| — | — | — |
| インシデントコマンダー | 深刻度、範囲、意思決定の記録 | 根拠なく侵害を確定する |
| 調査担当者 | タイムライン、クエリ、証拠ハッシュ | 元の証拠を直接修正する |
| サービス・プラットフォーム担当者 | 機能制限、デプロイ、復旧 | 証拠確保前にログを削除する |
| IAM・データ担当者 | トークン失効、権限・テナント確認 | LLMに最終的な認可判断を委任する |
| 個人情報・法務担当者 | 保存・通知要件の判断 | すべてのテレメトリーを無期限に保管する |
初期の深刻度は、「攻撃文字列が見られたか」よりも実際のインパクトで決定します。ブロックされた入力のみであれば、検出イベントである可能性があります。ブロック後にToolの実行、他のテナントデータの照会、シークレットの露出、または外部システム変更が確認された場合は、優先順位を上げる必要があります。
演習事件: ブロック後もToolが実行された
以下は演習用の仮定です。
- input_guard Spanはsecurity.decision=blockを記録した。
- 同じTrace内にtool_policyとtool_call Spanが続いている。
- Toolの結果が実際に外部変更を引き起こしたかはまだ不明である。
- 元のプロンプトと応答は観測ストレージに保存されていない。
この状態で「Guardrail迂回によりデータが流出した」と断定してはなりません。まず、3つの仮説を分離します。
- 計測エラー: Spanの順序や属性のみが誤って記録された。
- 制御フローの欠陥: ブロック決定後に実行停止が漏れた。
- 権限侵害: Toolが実際に許可されていない読み書きを実行した。
調査の目的は、最も刺激的な説明を選ぶことではなく、証拠によって仮説を一つずつ排除することです。
ステップ1: 事件番号と調査時間範囲を固定する
まず、事件番号、調査者、基準タイムゾーン、および最初のAlertを記録します。画面に表示される相対時間「10分前」ではなく、UTC絶対時刻を使用します。
incident_id: INC-LLM-2026-0007
detected_at: 2026-09-03T01:10:00Z
window_start: 2026-09-03T00:55:00Z
window_end: 2026-09-03T01:25:00Z
initial_signal: block 이후 tool_call Span 발견
Prometheusで事件前後のブロック率とTool呼び出し量を確認します。
sum(rate(llm_security_requests_total{decision="block"}[5m]))
/
sum(rate(llm_security_requests_total[5m]))
sum by (tool, result) (
increase(llm_tool_calls_total[30m])
)
メトリックは異常の時点と規模を知らせますが、個別のTraceIdをラベルとして含めません。これは、リクエストごとに異なる値をラベルとして使用すると、時系列の数が爆発的に増加するためです。
ステップ2: Traceで実行境界を確認する
Tempoでブロックされた入力Traceを見つけます。属性名は本演習の例であり、実際の計測スキーマに合わせる必要があります。
{ name = "input_guard" && span.security.decision = "block" }
Traceを開き、次の順序を確認します。
request
├─ authenticate
├─ input_guard decision=block
├─ tool_policy ← 존재하면 제어 흐름 의심
└─ tool_call ← 실제 실행·결과 확인 필요
Spanの開始・終了時刻、Status、service.name、デプロイバージョン、Tool名、および認可決定IDを記録します。プロンプト原文、アクセストークン、Toolの機密引数を証拠メモにコピーしないでください。必要な場合は、値の代わりに存在有無、長さ、分類、および別途保管場所を残します。
ステップ3: Lokiとダウンストリーム監査ログで実際の影響を検証する
同じTraceIdでアプリケーションログを検索します。TraceIdは、Lokiの高カーディナリティインデックスラベルよりも、構造化メタデータやログフィールドに配置する方が適切です。
{service_name="llm-security-app"}
| trace_id="조사할_TraceId"
アプリケーションログだけでToolの実行成功を確定しないでください。対象API、データベース、クラウド監査ログで、同じrequest_id、操作ID、または認可決定IDを相互確認します。
| 確認ポイント | 残すべき最小限の証拠 | 答えるべき質問 |
| — | — | — |
| アプリケーション | TraceId, RequestId, デプロイバージョン | ブロック後もコードパスは続行されたか? |
| Guardrail | ルールID、決定、ポリシーバージョン | どのポリシーが何を決定したか? |
| IAM・ポリシーエンジン | 主体、テナント、決定ID | 誰がどのような権限で承認されたか? |
| Toolゲートウェイ | Tool名、結果コード、対象分類 | 呼び出しは実際に送信されたか? |
| ダウンストリーム | 監査イベントID、変更前後の状態 | 読み書きは実際に反映されたか? |
| RAG | ドキュメントID・バージョン、テナントフィルター結果 | 他のテナントの資料が含まれていたか? |
LLMやGuardrailの判断は業務認可の証拠ではありません。最終的なアクセス権限は信頼できるアプリケーション/APIレイヤーとダウンストリームシステムが強制し、調査担当者はその認可記録を確認する必要があります。
ステップ4: オリジナルを改変しない証拠の束を作成する
調査結果は、後で同じ結論を再検討できるようにする必要があります。元のストレージを修正せずに、クエリ、検索時間範囲、エクスポートされた結果、設定・デプロイバージョン、およびハッシュを別の事件ディレクトリに保存します。
mkdir -p incident-INC-LLM-2026-0007/{queries,exports,config,notes}
cp prometheus-query.txt incident-INC-LLM-2026-0007/queries/
cp trace-summary.json incident-INC-LLM-2026-0007/exports/
cp redacted-events.jsonl incident-INC-LLM-2026-0007/exports/
cp deployment-version.txt incident-INC-LLM-2026-0007/config/
find incident-INC-LLM-2026-0007 -type f ! -name SHA256SUMS
-print0 | sort -z | xargs -0 sha256sum
> incident-INC-LLM-2026-0007/SHA256SUMS
tar --sort=name --mtime='UTC 1970-01-01'
--owner=0 --group=0 --numeric-owner
-czf INC-LLM-2026-0007-evidence.tar.gz
incident-INC-LLM-2026-0007
sha256sum INC-LLM-2026-0007-evidence.tar.gz
macOSでは、GNU sha256sumの代わりにshasum -a 256を使用できます。tarオプションも実装ごとに異なるため、運用環境でのサポート状況を確認してください。
notes/timeline.mdには、観測事実と解釈を分離して記述します。
[사실] 01:03:14Z input_guard가 block 기록
[사실] 01:03:14Z 같은 Trace에 tool_call Span 존재
[사실] Downstream 감사 로그에 write 이벤트 없음
[해석] 호출 준비 단계까지 진행됐으나 외부 변경 증거는 확인되지 않음
[미확인] 응답이 사용자에게 반환됐는지 Gateway 로그 확인 필요
ハッシュはファイルが変更されたかを確認する手段であり、収集プロセスの信頼性を自動的に保証するものではありません。収集者、収集時刻、元の場所、変換・マスキングプロセス、およびアクセス記録も一緒に残します。
テレメトリー自体が新たな情報漏洩経路にならないようにする
OpenTelemetryの公式セキュリティガイドラインは、テレメトリーが意図せずPII、アプリケーションデータ、ネットワークパターンを含む可能性があると警告しています。何が機密情報であるかをOpenTelemetryが自動的に判断することはできないため、実装者は最小限の収集・削除・ハッシュ化・編集ポリシーを定める必要があります。
基本的な証拠としては、以下を優先します。
- 原文の代わりにTraceId、ルールID、決定、処理段階
- システムプロンプトの内容の代わりに承認されたバージョンまたはハッシュ
- RAG原文の代わりにドキュメントID・バージョンとテナントフィルター結果
- Tool引数原文の代わりに作業種類、対象分類と結果コード
- ユーザー識別子の代わりに事件調査に必要な限定された仮名識別子
原文が本当に必要な場合は、一般的な観測ストレージとは分離された暗号化ストレージ、制限された役割、短い保存期間、および閲覧監査が必要です。法律・契約・規制要件は、組織ごとの担当者が判断する必要があります。
ステップ5: 証拠を確保した後、被害を制限する
Containment、すなわち被害制限は、「サーバーをすべて停止する」といった単一の行動ではありません。影響と業務継続性に合わせて、最も狭い安全措置から適用します。
| 確認された状況 | 優先措置例 | 確認すべき副作用 |
| — | — | — |
| ブロック後に特定のToolのみ呼び出し | 該当Toolルートを無効化 | 基幹業務の中断有無 |
| 特定ポリシーバージョンの欠陥 | 以前の承認バージョンにロールバック | 新ポリシーで防いでいたリスクの再露出 |
| トークン露出の可能性 | トークン失効・ローテーション、セッション終了 | 自動化・バッチ作業の失敗 |
| テナントフィルターの欠落 | Retrieval APIをフェイルクローズ | 検索機能全体の可用性 |
| 外部書き込みの確認 | 書き込み権限の削除、対象状態の復旧 | 証拠と正常な変更の区別 |
可能であれば、LLM全体を停止するよりも、Toolの書き込みを読み取り専用に変更したり、危険なルートを遮断したり、人間の承認を要求する形で機能を縮小します。ただし、シークレットの漏洩や広範なテナント境界の崩壊が疑われる場合は、より強力な隔離が必要です。
すべての緊急変更には、実行者、承認者、時刻、変更内容、ロールバック条件、および関連する事件番号を記録します。
ステップ6: 根絶と復旧を検証する
原因がブロック返却後に関数が終了しない制御フローの欠陥であった場合、単純なポリシー追加で終わらせてはなりません。コードでフェイルクローズを強制し、自動テストを追加します。
decision = input_guard.check(request)
if decision.blocked:
audit(decision)
return blocked_response()
# この行より下には、許可されたリクエストのみが到達するべきです。
result = tool_gateway.execute(authorized_request)
復旧前には、少なくとも4つのことを試験します。
- ブロックされたリクエストではtool_policyとtool_call Spanが生成されない。
- 許可されたリクエストは正常に動作し、ダウンストリームの認可を再度通過する。
- 他のテナントのRAG文書とTool対象にアクセスできない。
- Metric・Trace・LogとAlertが新バージョンでも接続される。
最初はカナリアまたは制限されたテナントにデプロイし、エラー率・ブロック率・Tool呼び出し量を観察します。「パッチデプロイ成功」は復旧完了ではありません。正常な機能、セキュリティ境界、および観測機能がすべて回復したときに終了します。
ステップ7: 事後分析を検出と設計改善にフィードバックする
事後報告書は、人を責める文書ではなく、次の事件の検出・対応時間を短縮するためのインプットです。
- 最初の信号と実際の影響は何だったか?
- どの証拠が結論を変えたか?
- 発見と遮断にそれぞれどれくらいの時間がかかったか?
- テレメトリーで不足していた、または過剰だったフィールドは何だったか?
- Runbook、Test、Alert、および権限境界を何に変更するか?
今回の事件では、以下の改善が自然です。
예방: block 이후 즉시 종료하는 공통 Middleware
탐지: blocked Trace에 tool_call이 존재하면 Critical Alert
제한: Tool Gateway에서 사용자·Tenant 권한을 다시 검사
검증: CI에 차단 후 Side Effect 부재 테스트 추가
운영: 사건 Query와 증거 Manifest 템플릿 표준화
演習完了チェックリスト
- 事件番号、担当者、UTC時間範囲、および初期仮説を記録した。
- Metricで異常時点と規模を確認した。
- Traceでブロック後の実行経路を確認した。
- アプリケーションとダウンストリーム監査ログを相互検証した。
- 観測事実、解釈、および未確認項目を分離した。
- オリジナルを修正せずにエクスポートとクエリを保存した。
- ファイルごとのハッシュと証拠束のハッシュを生成した。
- プロンプト・応答・トークン・PIIを基本証拠から除外した。
- 最も狭いContainmentとロールバック条件を記録した。
- パッチ後、ブロック・認可・テナント・観測回帰テストを実行した。
- 教訓をAlert、Test、Runbook、および権限設計に反映した。
まとめ
LLMセキュリティインシデント対応の核心は、モデルがなぜそのような文章を作成したのかを推測することではありません。どのような主体が、どのようなポリシーとデプロイバージョンでリクエストを行い、Guardrailとアプリケーションが何を決定し、Toolとダウンストリームが実際にどのような副作用を生み出したのかを証拠で結びつける必要があります。
Prometheusは事件の時点と規模を、Tempoはリクエストの実行経路を、Lokiと監査ログは決定の根拠と実際の影響を示します。これに、変更記録、ハッシュ、アクセス制御、復旧検証を加えることで、観測データは対応可能な証拠となります。最後のステップは常に同じ質問です。「次回は、より早く発見し、より狭く遮断し、より確実に復旧できるか?」
公式資料
- NIST SP 800-61 Rev.3: Incident Response Recommendations and Considerations for Cybersecurity Risk Management
- NIST AI 600-1: Generative Artificial Intelligence Profile
- NIST AI Risk Management Framework
- OpenTelemetry Security
- OpenTelemetry: Handling Sensitive Data
- OpenTelemetry Context Propagation
- Grafana Tempo TraceQL
- Grafana Loki LogQL
- Prometheus Querying Basics
※ インシデント対応・証拠保全・通知義務は、組織の産業、地域、契約、法的要件によって異なります。この記事のコマンドと保全項目は教育用の例であり、実際の事件では組織の承認されたインシデント対応手順と法務・個人情報担当者の指示を優先します。
カテゴリ: 一般IT/ITセキュリティ
タグ: LLM Security, Incident Response, Digital Forensics, OpenTelemetry, Loki, Tempo, Prometheus, NIST
コメントを残す