読者が抱える課題
LLM(大規模言語モデル)やAIエージェントが外部APIを呼び出して処理を実行する仕組みとして、Anthropicが提唱する MCP(Model Context Protocol) の活用が進んでいます。しかし、AIエージェントにAPIの操作権限を付与する際、以下のようなセキュリティ上の課題が発生します。
- APIキーやアクセス delegated token をMCPサーバー側またはクライアント側でどのように保持・管理すべきか分からない
- AIエージェントのプロンプトインジェクション等による過剰な操作(削除APIの意図しない実行など)を回避したい
- ユーザーごとのアクセス権限を保持したまま安全にツールを実行させたい
本記事では、MCPアーキテクチャにおける認証・認可のベストプラクティス、権限設計モデル、およびNode.jsによる具体的な実装例を解説します。
この記事で分かること
- MCPにおける認証(Authentication)と認可(Authorization)の基本分離構造
- APIキーのプロキシ管理パターンとユーザー委任(OAuth 2.0)パターン
- Minimum Privilege(最小権限の原則)を適用したMCPツール設計
- 導入時に確認すべきセキュリティ・チェックリスト
対象読者・前提条件
- 対象読者: LLMを活用した社内ツールやAIエージェントの構築を担当するWebエンジニア、インフラ・セキュリティ担当者
- 前提条件: REST API、OAuth 2.0、TypeScript/Node.jsの基本知識を有していること
※MCPの仕様は急速に更新される可能性があるため、実装時には公式のMCP仕様ドキュメント(modelcontextprotocol.io)も合わせてご確認ください。
MCP認証・権限設計の2大パターン
MCPにおいて、認証情報(API Key / Token)をどこで保持し、誰の権限として外部APIを叩くかは非常に重要です。主に以下の2つのパターンが存在します。
比較表
| 項目 | パターンA: サーバー統合型(システム共通権限) | パターンB: ユーザー委任型(OAuth/Passthrough) |
|---|---|---|
| 概要 | MCPサーバーが共通APIキーを保持してリクエストを代行 | ユーザー自身のトークンをMCPツール呼び出し時に伝播 |
| 利用ユースケース | 社内DBの参照、システム共通の通知送信など | GitHub、Slack、Google Driveなどの個人紐付け操作 |
| セキュリティリスク | 1つのプロンプト突破で全体権限が危険に晒される | トークンの有効期限切れやハンドシェイクの管理が必要 |
| 実装難易度 | 低(環境変数にAPIキーをセットするのみ) | 中〜高(認証フローとトークンストアが必要) |
実務において、更新・削除権限を伴うリソース操作には パターンB(またはコンテキスト制限を強めたパターンA) を採用することが推奨されます。
具体的な実装例(TypeScript / MCP SDK)
以下は、MCPツール定義時に「読み取り専用」と「変更操作」で権限を分離し、実行時にコンテキストチェックを行う実装例です。
※以下のコードは概念および動作構造を示すための実装例です。実際の運用環境に導入する際は、利用フレームワークや環境に合わせて検証を行ってください。
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
// 外部API実行用クライアント(独自実装の想定例)
interface UserContext {
userId: string;
scopes: string[];
}
// ツールごとの必要スコープ定義
const REQUIRED_SCOPES: Record<string, string> = {
"get_user_data": "read:user",
"update_user_status": "write:user"
};
const server = new Server(
{
name: "secure-api-mcp-server",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
}
);
// 1. 利用可能なツールの宣伝(読み取り権限と書き込み権限を明示)
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "get_user_data",
description: "ユーザーの基本情報を取得します(読み取り専用)",
inputSchema: {
type: "object",
properties: {
userId: { type: "string" }
},
required: ["userId"]
}
},
{
name: "update_user_status",
description: "ユーザーのステータスを変更します(書き込み権限要)",
inputSchema: {
type: "object",
properties: {
userId: { type: "string" },
status: { type: "string" }
},
required: ["userId", "status"]
}
}
]
};
});
// 2. ツール実行時の認可判定(擬似コード)
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
// 環境変数やヘッダー等からセッション情報を抽出する想定(独自実装部)
const userContext: UserContext = getContextFromEnvironment();
// スコープのチェック
const requiredScope = REQUIRED_SCOPES[name];
if (requiredScope && !userContext.scopes.includes(requiredScope)) {
throw new Error(`Permission Denied: Missing required scope [${requiredScope}]`);
}
switch (name) {
case "get_user_data":
// 安全な読み取り処理の実行
return { content: [{ type: "text", text: `User Data for ${args?.userId}` }] };
case "update_user_status":
// 書き込み処理の実行
return { content: [{ type: "text", text: `Updated ${args?.userId} status to ${args?.status}` }] };
default:
throw new Error(`Unknown tool: ${name}`);
}
});
function getContextFromEnvironment(): UserContext {
// 認証コンテキストを取得する処理のダミー実装
return {
userId: process.env.USER_ID || "guest",
scopes: (process.env.USER_SCOPES || "read:user").split(",")
};
}
導入時の注意点・失敗しやすいポイント
1. プロンプトインジェクションによる間接的権限昇格
AIエージェントが外部から取得したテキスト(メール本文やWebページ)に悪意ある指示が含まれていた場合、エージェントが「削除API」などの高リスクツールを誤って呼び出す危険があります。
対策: 高リスクな操作(Destructive Operations)を行うツールに対しては、MCPクライアント側(LLMの呼び出し元)で Human-in-the-loop(人間の承認ステップ) を挟む設計にしてください。
2. 環境変数への認証情報ベタ書き
MCPサーバーの構成ファイル(mcpServers 設定ファイル等)に平文で秘密鍵やAPI Tokenを直接記述してGit等にコミットしてしまうケースが散見されます。
対策: 認証情報はシークレット管理ツール(AWS Secrets Manager, HashiCorp Vault等)から動的に取得するか、実行時環境変数経由でのみ注入してください。
実務用 セキュリティ設計チェックリスト
設計・レビュー時に以下の項目を満たしているか確認してください。
- 最小権限: AIエージェントに渡すAPIトークンのスコープは必要最小限(Read-Onlyが標準)になっているか
- 承認フロー: 破壊的変更(DELETE, UPDATE, 送金等)を行うツールには人間の確認(Human-in-the-loop)が必須になっているか
- ログと監査: MCPツールが実行された際、「誰が(User)」「どのLLMの指示で」「何のパラメータを渡して」実行したかがログに残るか
- タイムアウト設定: 長時間動作するエージェントに対して、トークンの有効期限(TTL)を適切に短く設定しているか
- 入力バリデーション: MCPサーバー側で、LLMから渡された引数の型・形式チェック(ZodやJSON Schemaなど)を厳格に行っているか
まとめ
MCPを実務で安全に運用するためには、「LLMの判断を全面的に信頼しない」というゼロトラスト前提の設計が必要です。MCPサーバーを単なるAPIの土台とするのではなく、権限の分離・検証・ログ監査の層として位置付けることで、安全なAIエージェント活用が可能になります。