MCPの監査ログ設計:ツール呼び出しごとに記録すべき14項目・残してはいけない情報・8つのアラート【MCPセキュリティ/2026-07-28版】
リモートMCPサーバーの監査ログ設計を解説。2026-07-28版で非推奨になったLogging機能が監査ログにならない理由、記録すべき14項目、残してはいけない情報、動作確認済みのハッシュチェーン実装、OpenTelemetryとの対応、保管期間、8つのアラートまで。
最終更新:2026年10月17日 / 対象:MCP仕様 2026-07-28版(Tools、Logging、Authorization Security Considerations)、OWASP MCP Top 10(2025年版・ベータ)、OWASP Logging Cheat Sheet、OpenTelemetryのMCPセマンティック規約(Development)
結論(30秒で読める要約)
MCPの監査ログとは、リモートMCPサーバーに届いたリクエストごとに、構造化された改ざん検知可能な記録を1件ずつ残すことです。目的は、数週間後でも「誰が、どこから、どのツールを呼び、結果はどうだったか」に答えられるようにすること。MCPセキュリティの土台の一つで、OWASPは監査ログがない状態を「MCP08:2025 Lack of Audit and Telemetry」としてトップ10に入れています。
要点は次の6つです。
- クライアントではなく、ゲートウェイかサーバーで記録する。2026-07-28版の仕様はクライアントに「監査目的でツールの利用を記録すべき(SHOULD)」と書いていますが、すべての呼び出し元を見られるのはサーバー側だけです。
- 1リクエストあたり14項目を記録する。時刻、呼び出し元のID、種別、送信元IP、MCPメソッド、ツール名、引数のキー名、HTTPステータス、結果、処理時間、その時点のツール定義のハッシュなど(3章)。
- トークン・シークレット・引数の生の値は記録しない。代わりにキー名、呼び出し元のフィンガープリント、マスク済みのエラー文を残します(4章)。
- MCP標準のLogging機能を監査ログ代わりにしない。
notifications/messageは2026-07-28版で非推奨になり、そもそもクライアントへ送る仕組みで、クライアントが求めたときしか送られません。仕様は代替としてstderrとOpenTelemetryを挙げています。 - 改ざんを検知できる形で、必要な期間保管する。ハッシュチェーンかWORMストレージ、削除権限の分離、準拠すべき基準に合った保管期間(PCI DSSなら12か月)。
- 8つのアラートを設定する。まずは401の急増、スコープ拒否、新しい外部送信先、リリースしていないのに変わったツール定義から(9章)。
6章に、記録の生成・シークレットと制御文字の除去・行ごとのハッシュチェーンまで行うPythonモジュール(動作確認済み)を載せています。
目次
- MCPの監査ログとは
- 仕様とOWASPが実際に求めていること
- 監査レコードに入れる14項目
- 記録してはいけないもの
- どこで記録するか:クライアント・ゲートウェイ・サーバー・Egress
- 実装例(Python・動作確認済み)
- OpenTelemetryとの対応
- 改ざん検知・アクセス制御・保管期間
- 設定しておきたい8つのアラート
- 5つの質問でインシデントを再構成する
- 監査ログのチェックリスト(14項目)
- よくある失敗(症状→原因→対処)
- NodeFlareならこう解決できる
- よくある質問(FAQ)
1. MCPの監査ログとは
MCPの監査ログとは、認証の結果、ツールやリソースの呼び出し、認可の拒否、外部への通信といった、セキュリティ上意味のあるMCPの動きをサーバー側で記録し、後から黙って書き換えられないように保管したものです。用途は調査と説明責任で、デバッグ用ではありません。
「MCPのログ」と呼ばれるものは実は3種類あり、監査ログに当たるのは1つだけです。
| MCPのLogging機能 | オブザーバビリティのトレース | 監査ログ | |
|---|---|---|---|
| 中身 | サーバーがクライアントへ notifications/message を送る |
OpenTelemetryなどのスパンとメトリクス | セキュリティ上のイベントごとの変更不可な記録 |
| 読む人 | クライアントの画面・ユーザー | SRE・開発者 | セキュリティ担当・インシデント対応・監査人 |
| 制御する側 | クライアント(リクエストごとにログレベルを指定) | 自社 | 自社 |
| 2026-07-28版での位置づけ | 非推奨(SEP-2577)。新規実装は採用すべきでない(SHOULD NOT) | 構造化された可観測性の代替として仕様が推奨 | 仕様には定義なし。OWASP MCP08が推奨 |
| 監査に使えるか | 使えない(クライアント側で止められ、記録もクライアントに残る) | 一部のみ(サンプリングと短い保管期間で抜けが出る) | 使える |
この非推奨化は見落とされがちです。2025年の解説記事には「可視化にはMCPのloggingを使う」と書かれたものがまだ多くあります。しかし2026-07-28版では、_meta に io.modelcontextprotocol/logLevel を付けていないリクエストに対してサーバーは notifications/message を送ってはいけない(MUST NOT)ことになっています。攻撃者のクライアントはこれを付けなければよいだけです。監査の記録は自分の側に持つ必要があります。
2. 仕様とOWASPが実際に求めていること
MCPの仕様に「監査ログ」という章はありませんが、複数のページの規定を合わせると要件ははっきりします。残りはOWASP MCP08が補っています。
| 規定 | 出典 | 内容 |
|---|---|---|
| ツール利用の記録 | MCP Tools(Security Considerations) | クライアントは「監査目的でツールの利用を記録」すべき(SHOULD) |
| アクセス制御とレート制限 | MCP Tools(Security Considerations) | サーバーは適切なアクセス制御とツール呼び出しのレート制限を実装しなければならない(MUST)。ここで生じる拒否こそ記録したいイベント |
| ログにシークレットを入れない | MCP Logging(Security) | ログには資格情報・シークレット、個人を特定できる情報、攻撃の手がかりになる内部情報を含めてはならない(MUST NOT) |
| ログ中のトークンは盗まれる | MCP Authorization Security Considerations | サーバーでキャッシュ・記録されたトークンを入手した攻撃者は「正当に見える」リクエストを送れる |
| 機微な引数をヘッダーに出さない | MCP Tools(x-mcp-header) |
パスワード、APIキー、トークン、個人情報を Mcp-Param-* ヘッダーに写すべきでない(SHOULD NOT)。中継機器から見え、ログにも残りやすい |
| 構造化と一元化 | OWASP MCP08 | エージェントの動きが構造化・一元化された形式で記録されていないことを脆弱性の兆候とする |
| 改ざん検知できる保管 | OWASP MCP08 | ログをHMACやSHA-256でハッシュし、追記専用または一度だけ書き込めるメディアに保管する |
| 止めずにマスクする | OWASP MCP08 | プライバシーを理由にログを丸ごと止めることを脆弱性の兆候として挙げている |
| 削除の保護 | OWASP MCP08 | ログの削除や保管期間の変更には2人の承認を求める |
ここから言えることは2つです。プライバシーはログを止める理由にならないこと(OWASPがそれ自体を弱点と明言しています)。そしてクライアント側のSHOULDだけでは、サーバーを運営する側には足りないことです。呼び出し元のクライアントは自分では管理できないからです。
3. 監査レコードに入れる14項目
役に立つ監査レコードは、「いつ・誰が・どこから・何を・結果はどうだったか」に、絞り込みに使えるフィールドで答えます。下の表は、OWASP Logging Cheat Sheetの「when, where, who, what」、OWASP MCP08の推奨フィールド、MCP特有の項目を合わせたものです。
| フィールド | 例 | 必要な理由 |
|---|---|---|
ts |
2026-10-17T05:01:26Z |
UTC・ISO 8601。他システムと時系列を突き合わせるため |
event_id |
UUID | 重複排除と、チケットからの参照 |
server_id とバージョン |
invoices-mcp@1.8.2 |
OWASPの「where」。どのサーバーのどのビルドが応答したか |
caller_kind |
api_key、oauth、anonymous |
人・機械・未認証の探索を分ける |
caller_id |
キーのフィンガープリント、またはOAuthの sub と client_id |
資格情報そのものを残さずに「誰か」を特定する |
client_ip |
203.0.113.7 |
見慣れないネットワークはキー流出の最初のサイン |
mcp_method |
tools/call、tools/list、resources/read |
操作の種類 |
tool_name・リソースURI |
refund_invoice |
インシデント対応で最もよく検索される項目 |
arg_keys |
["invoice_id","amount"] |
値を残さずに呼び出しの形を残す |
http_status |
200、401、403、429 |
認証・認可の結果 |
outcome |
ok、auth_failed、scope_denied、rate_limited、error |
自社で正規化した結果。ステータスコードよりアラートを書きやすい |
duration_ms |
84 |
大量エクスポートや暴走ループの検知 |
tools_list_sha256 |
返した tools/list 結果のハッシュ |
その呼び出し時点で有効だったツール定義を特定できる(ラグプルの証拠) |
trace_id |
W3Cのトレース ID | オブザーバビリティのスパンと結びつける |
いくつか補足します。
- セッションIDに頼らない。2026-07-28版のToolsページには「MCPにはプロトコルレベルのセッションがない」と明記されています。新しいクライアントが送らないかもしれないセッションヘッダーではなく、
caller_id、trace_id、時間帯で突き合わせます。 - 成功だけでなく拒否を記録する。ツールのコードに届かなかった403こそ、エージェントが本来の仕事以上のことをしようとした証拠で、ログの中で最も価値のある1行です。
tools_list_sha256は安く付けられる。デプロイごとにtools/listの応答を1回ハッシュし、全レコードに付けるだけです。「ツールの説明文は変わったか」という問いが検索1回で済みます。この問いが重要な理由はMCPのtool poisoning対策ガイドで解説しています。
4. 記録してはいけないもの
監査ログはすべての呼び出し元のデータが集まるため、それ自体が狙われます。OWASP Logging Cheat Sheetは、アクセストークン、セッション識別子、パスワード、接続文字列、暗号鍵、決済カード情報などを、通常はそのまま記録すべきでないものとして挙げ、削除・マスク・ハッシュ化・暗号化を求めています。
| 記録しない | 理由 | 代わりに残すもの |
|---|---|---|
Authorization ヘッダー、APIキー、OAuthトークン |
記録されたトークンはそのまま使える(MCP Authorization Security Considerations) | caller_kind と、キーのSHA-256の短いフィンガープリント。または検証済みトークンの sub と client_id |
| ツール引数の生の値 | 顧客データや検索語、モデルが貼り付けたシークレットが入ることがある | arg_keys。リスクの高いツールだけ、レコードIDなど機微でない値を許可リスト方式で残す |
| ツール結果の生の値 | 個人情報や注入された指示が含まれうる | 結果のバイト数、isError、必要なら何を返したか証明するための結果ハッシュ |
| 下流サービスの資格情報 | 1件の漏えいで背後のSaaSまで露出する | 使ったシークレットの名前だけ。値は残さない |
機微な項目の Mcp-Param-* ヘッダー値 |
ロードバランサーやWAFは既定でヘッダーを記録する | そもそも機微な引数に x-mcp-header を付けない(仕様:SHOULD NOT) |
| サニタイズしていない文字列 | ツール名、エラー文、User-Agentは攻撃者が操作でき、改行で偽のログ行を作れる | 書き込む前に制御文字を除去し、長さを制限する(OWASP:CR、LF、区切り文字をサニタイズ) |
| プロンプト全文やモデルのコンテキスト | 個人データと信頼できないテキストが混ざり、法的に必要なことも少ない | 残すとしても、クライアント側のAIテレメトリーで別の保管期間・権限のもとで扱う |
特に注意したいのが、JSON-RPCのリクエストをそのまま書き出すデバッグログです。OWASP MCP01には、生のMCPペイロードを含むデバッグログからトークンが漏れるシナリオがそのまま載っています。本番のログレベルでこれが起きないことを確認してください。
5. どこで記録するか:クライアント・ゲートウェイ・サーバー・Egress
MCPのやり取りは層ごとに見える範囲が違い、1つの層ですべてを見ることはできません。
| 層 | 見えるもの | 見えないもの | 管理者 |
|---|---|---|---|
| MCPクライアント(Claude、IDE、エージェント) | ユーザーのプロンプト、モデルが選んだツール、人の承認 | 同じサーバーを呼ぶ他のクライアント | 利用者側の組織 |
| サーバー前段のゲートウェイ・リバースプロキシ | 全リクエスト、認証結果、送信元IP、メソッド、ツール名、サーバー到達前の拒否 | ツール内部で何をしたか | 自社 |
| MCPサーバーのコード | 業務上の結果、下流APIの呼び出し、触ったレコード | 手前で拒否されたリクエスト | 自社 |
| Egress(ネットワーク)層 | サーバーが名前解決・接続した外部の宛先すべて | 突き合わせない限り、どのリクエスト由来か | 自社 |
社内SaaSをMCPで公開するチームなら、最低限はゲートウェイ層とEgress層です。書き込み系・破壊的なツールについては「どのレコードを変えたか」が重要なので、サーバー側の記録も加えます。ツール単位のスコープをゲートウェイで強制すると、拒否も一貫して記録できます。スコープ付きの資格情報の発行方法はMCPのAPIキー管理プレイブックにまとめています。
6. 実装例(Python・動作確認済み)
次のモジュールは、3章のレコードを組み立て、シークレットと制御文字を取り除き(4章)、JSON Lines形式のファイルに1行ずつ追記します。各行には、前の行のHMACも含めて計算したHMACを付けるので、どこか1行でも書き換えたり消したりすると、そこから先のチェーンが壊れます。Python 3.13で動作を確認しました。
"""Audit records for a remote MCP server: one JSON line per request, no secrets, hash-chained."""
import hashlib, hmac, json, re, time, uuid
CHAIN_KEY = b"load-from-your-secret-store" # HMAC key; never hard-code in production
SECRET_PATTERNS = re.compile(
r"(Bearer\s+[A-Za-z0-9._~+/=-]+|ghp_[A-Za-z0-9]{36}|sk-[A-Za-z0-9_-]{20,}|AKIA[0-9A-Z]{16})")
CONTROL_CHARS = re.compile(r"[\r\n\t\x00-\x1f\x7f]")
def clean(value: str, limit: int = 200) -> str:
"""Strip CR/LF and control chars (log injection), mask token shapes, cap length."""
value = CONTROL_CHARS.sub(" ", str(value))
return SECRET_PATTERNS.sub("[REDACTED]", value)[:limit]
def fingerprint(token: str) -> str:
"""Stable, non-reversible caller key id: first 12 hex chars of SHA-256."""
return hashlib.sha256(token.encode()).hexdigest()[:12]
def build_record(*, token, caller_kind, client_ip, method, tool, arguments,
status, outcome, duration_ms, tools_list_hash=None, error=None):
return {
"ts": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
"event_id": str(uuid.uuid4()),
"caller_kind": caller_kind, # api_key | oauth | anonymous
"caller_id": fingerprint(token) if token else None,
"client_ip": client_ip,
"mcp_method": method, # e.g. tools/call
"tool_name": clean(tool) if tool else None,
"arg_keys": sorted(arguments)[:50] if arguments else [], # key names only
"http_status": status,
"outcome": outcome, # ok | auth_failed | scope_denied | rate_limited | error
"duration_ms": duration_ms,
"tools_list_sha256": tools_list_hash, # which tool definitions were live
"error": clean(error) if error else None,
}
def append(path: str, record: dict, prev_mac: str) -> str:
"""Write one line whose MAC covers the previous line's MAC (tamper-evident chain)."""
record["prev_mac"] = prev_mac
body = json.dumps(record, sort_keys=True, separators=(",", ":"))
mac = hmac.new(CHAIN_KEY, body.encode(), hashlib.sha256).hexdigest()
with open(path, "a", encoding="utf-8") as f:
f.write(json.dumps({"r": record, "mac": mac}) + "\n")
return mac
def verify(path: str) -> int:
"""Return the line number of the first broken link, or 0 if the chain is intact."""
prev = "genesis"
for n, line in enumerate(open(path, encoding="utf-8"), 1):
row = json.loads(line)
body = json.dumps(row["r"], sort_keys=True, separators=(",", ":"))
good = hmac.new(CHAIN_KEY, body.encode(), hashlib.sha256).hexdigest()
if row["r"].get("prev_mac") != prev or not hmac.compare_digest(good, row["mac"]):
return n
prev = row["mac"]
return 0
使い方は、HTTPミドルウェアで認証・認可の判定が終わった後(拒否も記録するため)に build_record を呼び、直前の呼び出しが返したMACを渡して append します。最初のMACは "genesis" です。3件のリクエストと2種類の改ざんで試した結果は次のとおりです。
| テスト | 入力 | 結果 |
|---|---|---|
| 通常の呼び出し | get_invoice。引数 api_key に sk-... の値 |
arg_keys: ["api_key","invoice_id"] だけが残り、値はファイルに書かれなかった |
| ログインジェクション | ツール名 refund_invoice\r\nFAKE ok |
refund_invoice FAKE ok として1行で記録。偽の2行目は作られなかった |
| エラー文中のトークン | ...header was Bearer eyJhbGciOi.abc.def |
header was [REDACTED] として記録 |
| 未認証の探索 | トークンなしの tools/list |
caller_kind: anonymous、http_status: 401、outcome: auth_failed |
| 改ざんなし | verify() |
0(チェーンは正常) |
| 1項目の書き換え | 2行目の outcome を scope_denied から ok に変更 |
verify() が 2 を返した |
| 1行の削除 | 中間の行を削除 | verify() が 2 を返した |
本番で使う前の注意点です。
- 末尾の切り詰めはチェーンだけでは検知できない。最後の数行を消すと、短いが正しいチェーンが残ります。最新のMACを定期的に別システムへ写すか、ファイルをWORMストレージに書き込んで、本来の末尾が分かるようにしてください。
- 正規表現は一般的なトークンの形しか拾わない。自社のキーのプレフィックス(例:
acmemcp_live_)を追加し、そもそもclean()にシークレットを渡さない設計にします。 - HMACの鍵そのものがシークレット。シークレットストアから読み込み、ログを削除できる人とは別の人が管理します。
7. OpenTelemetryとの対応
OpenTelemetryにはMCP向けのセマンティック規約があり、MCP仕様も構造化された可観測性の行き先としてOpenTelemetryを挙げています。監査項目の多くはスパン属性として運べるので、監査に関わるスパンを十分な保管期間のあるストアへ送る構成が取れます。
| 監査項目 | OpenTelemetryの属性 |
|---|---|
mcp_method |
mcp.method.name(定義済みの値に tools/call、tools/list、resources/read など) |
tool_name |
gen_ai.tool.name |
| リソースURI | mcp.resource.uri |
| プロトコルバージョン | mcp.protocol.version |
client_ip |
client.address |
trace_id |
スパンコンテキスト(W3C Trace Context) |
出典から読み取れる注意点が3つあります。
- ステータスはDevelopment。OpenTelemetryの属性レジストリでは、これらのMCP属性は別のGenAIセマンティック規約リポジトリへ移動したと表示されており、GenAIとMCPの規約は2026年時点でもDevelopmentのままです。名前が変わる前提で、計装ライブラリのバージョンを固定してください。
mcp.session.idは空かもしれない。セッションIDを表す属性ですが、2026-07-28版のプロトコルにはプロトコルレベルのセッションがありません。監査の必須項目にはしないでください。- トレースはサンプリングされるが、監査ログはされてはいけない。調査で頼る記録は、別のパイプラインに流すか、
tools/call・401・403のスパンを100%残すテールサンプリングのルールを設けます。
8. 改ざん検知・アクセス制御・保管期間
監査ログは、調査対象の人が書き換えられない状態で初めて役に立ちます。OWASP MCP08は追記専用または一度だけ書き込めるストレージ、ハッシュ化、削除時の2人承認を求め、Logging Cheat Sheetは「ログへのアクセスはすべて記録・監視すること」と付け加えています。
| 管理策 | 最低限 | より良い形 |
|---|---|---|
| 完全性 | 6章のHMACチェーン | ほぼリアルタイムでWORMストレージ(コンプライアンスモードのロックをかけたオブジェクトストレージなど)へ送る |
| 閲覧 | 全項目を読めるのはセキュリティチームだけ | OWASP MCP08の推奨どおり、運用ダッシュボードとセキュリティ調査で権限を分ける |
| 削除 | 手作業で消す人がいない | 保管期間の変更と削除には2人の承認が必要 |
| 転送 | ログストアまでTLS | 署名付きのバッチ |
| 監視の監視 | 取り込み量がゼロになったらアラート | 保管設定が変わったらアラート |
MCPの監査ログをどれだけ保管するかは、MCPではなく準拠すべき基準で決まります。
| 根拠 | 保管期間 |
|---|---|
| PCI DSS v4.0(要件10.5.1)。MCPのツールがカード会員データ環境に触れる場合 | 最低12か月。直近3か月はすぐ参照できる状態 |
| OWASP MCP08の例示 | 「PCI DSS: 1 year minimum」 |
| 自社のインシデント対応 | 資格情報の漏えいから発覚まで、さらに調査に必要な期間をカバーできる長さ |
| GDPRや日本の個人情報保護法などのプライバシー法令 | 必要なものを必要な期間だけ。識別子は仮名化する(OWASP MCP08) |
現実的な分け方は、ログツールで30日検索できる状態にし、12か月分は安いアーカイブに置くことです。どちらにも入れる前に項目単位のマスクをかけておきます。
9. 設定しておきたい8つのアラート
アラートのルールを書いて初めて、ログは検知の仕組みになります。下のしきい値は標準ではなく、私たちが勧める出発点です。2週間ほど運用して、自社のトラフィックに合わせて調整してください。
| # | アラート | 最初の条件 | 考えられる意味 |
|---|---|---|---|
| 1 | 認証失敗の急増 | 同じIPから5分間に 401 が20回超 |
キーの総当たり、または失効済みキーがまだ使われている |
| 2 | スコープ拒否 | 書き込み系・外部送信系のツールで scope_denied が1件でも出た |
プロンプトインジェクションによる誘導、または盗まれたキーでの探索 |
| 3 | 新しい送信元ネットワーク | 有効な caller_id が、過去30日に見ていないIP帯から来た |
発行先のマシンからキーが持ち出された |
| 4 | 予定外の時間帯の機械アクセス | ジョブ用キーがスケジュール外の時間に使われた | ジョブ以外への流用 |
| 5 | ツール定義の変化 | デプロイがないのに tools_list_sha256 が変わった |
ラグプルや未レビューの変更 |
| 6 | 新しい外部送信先 | Egress層で、そのサーバーの許可リストにないホスト名が出た | 情報の持ち出し、または依存パッケージの侵害 |
| 7 | 読んでから送るパターン | 同じ呼び出し元が、非公開データの読み取りツールの数分後に外部送信ツールを呼んだ | ツール出力経由の注入(OWASP MCP06のIntent Flow Subversion) |
| 8 | 沈黙 | 通常の業務時間帯に監査レコードがゼロ | ログが止められた、または壊れた(OWASP MCP08の検知サイン) |
忘れられがちなのが8番です。OWASP MCP08は「利用中の時間帯にログがないこと」や、テレメトリーの取り込み量の急減を、それ自体が検知のサインだとしています。
10. 5つの質問でインシデントを再構成する
キーが漏れたり、エージェントがおかしな動きをしたりしたとき、ログは次の5つの質問に順番に答えられる必要があります。答えられない質問があれば、それが次に直すべきログの穴です。
- どの資格情報か。
caller_id(またはOAuthのsubとclient_id)で絞り、最初と最後の利用を確認します。 - どこからか。対象期間の
client_ipを集計します。新しいIP帯は不正利用の手がかりです。 - 何を呼んだか。
tool_nameとoutcomeで集計し、書き込み系と外部送信系を先に確認します。 - どのツール定義のもとでか。期間中の
tools_list_sha256を見て、ラグプルの可能性を判断します。 - データはどこへ行ったか。サーバーと時刻でEgressの記録と突き合わせます。許可リスト外の宛先があれば、そこが調査の起点です。
その後、呼ばれたツールが使う下流の資格情報をローテーションし、原因になった穴を塞ぎます。
11. 監査ログのチェックリスト(14項目)
記録の範囲
- [ ] MCPエンドポイントへのすべてのリクエストで1件ずつ記録している(
401、403、429を含む) - [ ] クライアントだけでなく、ゲートウェイかサーバーで記録している
- [ ] 書き込み系・破壊的なツールは、変更した業務オブジェクトのIDも記録している
- [ ] サーバーごとに外部送信先を記録している
記録の中身
- [ ] 3章の14項目をUTCで記録している
- [ ] トークン、APIキー、引数や結果の生の値を保存していない
- [ ] リクエスト由来の文字列から制御文字を除き、長さを制限している
- [ ] 機微な引数を
Mcp-Param-*ヘッダーに写していない - [ ] 非推奨になったMCPのLogging機能を監査証跡として扱っていない
完全性と運用
- [ ] ハッシュチェーン、または追記専用・WORMストレージで保管している
- [ ] ログの削除と保管期間の変更には2人の承認が必要
- [ ] 保管期間が準拠すべき基準を満たしている(PCI DSSの対象なら12か月など)
- [ ] 9章の8つのアラート(沈黙のアラートを含む)を設定している
- [ ] 5つの質問による再構成を一度は訓練している
12. よくある失敗(症状→原因→対処)
| 症状 | 原因 | 対処 |
|---|---|---|
| 「MCPのログはある」のに、他チームのエージェントの記録がない | 自社のクライアントだけで記録している | サーバー前段のゲートウェイで記録する |
| 調査中にログストアからトークンが見つかる | デバッグレベルでリクエストを丸ごと出力している | arg_keys とフィンガープリントを残し、本番ではペイロード出力を禁止する |
| 拒否された呼び出しがログにない | 認可の後、ツールのコード内で記録している | 認可の判定後、応答を返す前にミドルウェアで記録する |
outcome=ok のような偽の行が混ざる |
ツール名やエラー文をサニタイズしていない | 書き込む前にCR/LFと制御文字を除去する |
| ツールの説明文が変わったか分からない | 呼び出しとツール定義が結びついていない | 全レコードに tools_list_sha256 を付ける |
| トレースに呼び出しの10%しか残っていない | オブザーバビリティ側のヘッドサンプリング | 監査レコードはサンプリングしない別のパイプラインに流す |
| インシデントが始まった日でログが途切れている | 攻撃者または設定ミスでログが止まった | 沈黙のアラートを設定し、最新のチェーンMACを別の場所に保管する |
| 新しいクライアントで突き合わせができない | 2026-07-28版にはないセッションIDに頼っている | 呼び出し元、トレースID、時間帯で突き合わせる |
13. NodeFlareならこう解決できる
NodeFlareは、各MCPサーバーの前段にプロキシを置き、すべてのリクエストを、あなたのコードに届く前に認証・認可・記録します。この記事で挙げたリスクと、ドキュメントに記載された機能の対応は次のとおりです。
| この記事のリスク | NodeFlareの機能 | 内容 |
|---|---|---|
| サーバー側にリクエストごとの記録がない(3章・5章) | リクエストログ | リクエストごとに mcp_method、tool_name、arg_keys、http_status、outcome、duration_ms、caller_kind、client_ip、req_bytes、res_bytes、user_agent、session_id、error_message を記録。行をクリックするとRequest・Identity・Resultの詳細を確認できる |
| 引数やシークレットがログに残る(4章) | キー名のみの記録、ビルドログのマスク、暗号化されたシークレット | リクエストログには引数のキー名だけを残し、値は残さない。環境変数は保存時に暗号化され、VMの起動時にだけ注入され、ビルド引数・イメージレイヤー・ログには残らない。ビルドログでは既知のトークンの形をマスクする |
| 誰が呼んだか分からない(3章) | リクエストごとの呼び出し元 | caller_kind は api_key、oauth、anonymous のいずれか。APIキーは mcp_* 形式のBearerトークンでハッシュ化して保存。OAuthのアクセストークンはプロキシが検証する |
| 拒否がログに残らない(12章) | プロキシでのスコープ確認 | tools:call:<ツール名>(例:tools:call:get_weather)などのスコープを毎リクエスト確認。拒否された呼び出しはJSON-RPCエラーになってサーバーに届かず、outcome に scope_denied などとして残る |
| 調査に時間がかかる(10章) | 検索と期間指定 | tool_name、error_message、mcp_method、outcome、client_ip を大文字小文字を区別せずに検索できる。期間は1h・24h・7d・30d(Freeは1h固定)。保管期間はFreeで7日、Pro/Teamで30日 |
| 外部送信先が分からない(アラート6) | Egress制御 | off(既定)、observe(サーバーが名前解決した宛先を記録し、Logs > Egressで確認)、enforce(リスト外を遮断)の3モード。許可リストは最大100件。判定はDNSのホスト名で行い、通信内容は復号しない |
| キーの総当たり(アラート1) | 総当たりのロックアウト | 同じIPから誤ったキーが繰り返し送られると、そのIPを10分間ロックする |
| 漏れたキーが別のネットワークから使われる(アラート3) | IP許可リスト | ポリシーを restricted にしてIPv4/IPv6のCIDRを登録。認証より前に確認され、リストが空ならすべて拒否する |
| エージェントの暴走ループ | レート制限と利用上限 | IP単位の毎分の上限と、ワークスペース単位の月間クォータ(rate_limited)。有料プランでは月額の利用上限を設定でき、上限に達するとHTTP 402を返す |
自社で担う部分もあります。NodeFlareのドキュメントには、ログのエクスポート、SIEMへの転送、OpenTelemetryへのエクスポートについての記載はなく、保管期間も最長30日です。12か月の保管、改ざん検知できる保管、9章のようなシステム横断のアラートが必要なら、6章のモジュールなどでサーバー側にも独自の記録を残してください。また、NodeFlareはモデル内部でのプロンプトインジェクションそのものは防げません。スコープ、Egress制御、ログで、乗っ取られたエージェントにできることを狭め、その動きを見えるようにします。
最小の始め方:
- MCPサーバーをデプロイし(既定で非公開)、呼び出し元ごとに、必要な
tools:call:<ツール名>スコープだけを付けたmcp_*キーを発行する。 - Egress制御を1週間
observeにし、Logs > Egressで想定どおりの宛先を許可してからenforceに切り替える。 - 毎週リクエストログで
401とscope_deniedを確認し、スコープを絞り込む。
1サーバーまでは無料で試せます:NodeFlareを始める
14. よくある質問(FAQ)
MCPの監査ログには何を記録すればいい?
最低限、UTCのタイムスタンプ、呼び出し元の種別とID(トークンではなくフィンガープリント)、送信元IP、MCPメソッド、ツール名、引数のキー名、HTTPステータス、正規化した結果、処理時間です。さらにその時点の tools/list 結果のハッシュを付けると、各呼び出しでどのツール定義が有効だったかを証明できます。
MCPの仕様で監査ログは必須?
MUSTではありません。2026-07-28版のToolsページでは、クライアントは監査目的でツールの利用を記録すべき(SHOULD)とされ、サーバーにはアクセス制御とレート制限がMUSTで求められています。OWASPのMCP Top 10は監査とテレメトリーの欠如をMCP08としてトップ10に入れているので、本番運用では必須と考えるべきです。
MCPのlogging機能は非推奨になった?
はい。MCPのLogging機能(notifications/message)は、プロトコルバージョン2026-07-28でSEP-2577により非推奨になりました。新規実装は採用すべきでなく、既存の実装はstdioなら stderr、構造化された可観測性ならOpenTelemetryへ移行すべきとされています。仕様には少なくとも12か月は残ります。
MCPのツール引数や結果はログに残すべき?
既定では残すべきではありません。引数や結果には顧客データや、モデルが貼り付けたシークレットが含まれることがあります。引数のキー名、結果のサイズ、エラーの有無を残し、リスクの高いツールだけ、レコードIDのような機微でない値を許可リスト方式で残します。
MCPの監査ログはどのくらい保管すればいい?
準拠すべき基準に合わせます。PCI DSS v4.0は監査ログを最低12か月、うち直近3か月はすぐ参照できる状態で保管するよう求めており、OWASP MCP08も「1年以上」を例に挙げています。そうした要件がなければ、資格情報の漏えいから発覚・調査までをカバーできる期間を目安にします。
MCPの監査ログを改ざん検知できるようにするには?
この記事のPythonモジュールのように、前のレコードのHMACも含めて各レコードのHMACを計算するハッシュチェーンにし、最新のハッシュを別システムに写します。さらに、追記専用または一度だけ書き込めるストレージに保管し、削除や保管期間の変更には2人の承認を求めます。これはOWASP MCP08の推奨でもあります。
MCPの監査ログにOpenTelemetryは使える?
転送と突き合わせには使えます。OpenTelemetryは mcp.method.name などのMCP属性を定め、ツールには gen_ai.tool.name を使いますが、規約はまだDevelopmentで、トレースは通常サンプリングされます。監査レコードは、独自の保管期間を持つサンプリングなしのパイプラインに残してください。
MCPのログでアラートを出すべきイベントは?
まずは 401 の急増、書き込み系・外部送信系ツールでのスコープ拒否、新しいネットワークからの有効なキー、デプロイなしのツール定義の変化、新しい外部送信先です。加えて、監査ログの量がゼロになったときもアラートを出します。ログが止まったこと自体が侵害のサインだからです。
MCPの監査ログはクライアント・ゲートウェイ・サーバーのどこで取るべき?
まずゲートウェイかサーバーです。拒否したリクエストも含め、すべての呼び出し元が見えるのはこの層だけです。書き込み系ツールにはサーバーのコード側の記録を、外部への通信にはEgressの記録を加えます。クライアント側のログも参考になりますが、管理しているのはクライアントを動かす側です。
セッションIDなしでMCPのリクエストを突き合わせるには?
呼び出し元のID、トレースID、時間帯を使います。2026-07-28版の仕様は、MCPにはプロトコルレベルのセッションがないと明記しているため、新しいクライアントではセッションヘッダーがないこともあります。クライアントから伝播したW3CのトレースIDと、キーのフィンガープリントやOAuthの sub を組み合わせれば、確実に突き合わせられます。
まとめ
MCPの監査ログとは、すべての呼び出し元が見えるサーバー側で、リクエストごとに構造化・マスク済み・改ざん検知可能な記録を1件残すことです。誰が、どこから、どのメソッドとツールを、どの引数キーで呼び、結果はどうで、どのツール定義が有効だったかを記録し、トークンや生のペイロードは残しません。非推奨になったMCPのLogging機能を監査証跡と取り違えず、OpenTelemetryは突き合わせに使いつつ唯一の記録にはせず、基準が求める期間だけ保管します。そのうえで、沈黙を含む8つのアラートを設定してください。MCPセキュリティの他の論点は、MCPのtool poisoning対策ガイドとMCPのAPIキー管理プレイブックを、Mcp-Param-* ヘッダーがどう送られるかはMCP Streamable HTTPの解説をご覧ください。
参考資料(一次情報)
- MCP Specification 2026-07-28: Tools(Security Considerations、x-mcp-header)
- MCP Specification 2026-07-28: Logging(非推奨)
- MCP Specification 2026-07-28: Authorization Security Considerations
- OWASP MCP Top 10
- OWASP MCP08:2025 Lack of Audit and Telemetry
- OWASP Logging Cheat Sheet
- OpenTelemetry attribute registry: MCP
- OpenTelemetry GenAI semantic conventions repository
- NodeFlare Docs
