AIエージェント認証・認可ガイド: プロダクションでOAuth、ツール呼び出し権限、セキュリティを正しく実装する方法
AIエージェントが14,000人の顧客にSlackメッセージを送信した。注文状況確認用のプロダクションサポートエージェントがプロンプトインジェクションを受けて、一括メッセージングAPIにアクセスした。認証情報はあった。権限もあった。誰も承認していない。エージェントは技術的な認可範囲内で行動した。ただそれをやるべきじゃなかっただけだ。
これがAIセキュリティの新たな前線です。私たちはSQLインジェクション、XSS、CSRFからWebアプリケーションを守るために何年も費やしてきました。しかし今は、APIキー、OAuthトークン、データベース認証情報を持ち歩き、どのツールを呼び出してどのデータにアクセスするかを自ら判断する自律システムをデプロイしています。攻撃面はモデルの重みではありません。実行レイヤーです。プロンプトインジェクション、ゴールハイジャック、あるいは単純な設定ミスで操作可能な認証情報、権限、ツールアクセスこそが、今まさに直面している本当の脅威なのです。
このガイドでは、プロダクションAIエージェントのセキュリティアーキテクチャ全体をカバーします。アイデンティティ管理、OAuth 2.1委任認可、きめ細かなツール権限、MCPゲートウェイ適用、Human-in-the-Loopパターン、そして安全なエージェントデプロイと事故を未然に防ぐための多層防御戦略までを解説します。
従来の認証がAIエージェントで通用しない理由
インターンにAWSのルート認証情報を渡して「いい感じに使って」とは言わないですよね。でも多くのチームがAIエージェントに対してまさにそれをやっている。従来のサービスアカウントモデルがなぜ崩壊するのか、見ていきましょう:
エージェントは非決定論的アクター
従来のマイクロサービスは毎回同じAPIコールを行う。ソースコードを読めば振る舞いを監査できる。AIエージェントは根本的に異なる:
従来のサービス:
入力: "注文 #12345を取得"
→ 常に呼び出し: GET /api/orders/12345
→ 予測可能、監査可能
AIエージェント:
入力: "この顧客の注文を手伝って"
→ GET /api/orders/12345を呼ぶかもしれない
→ POST /api/refundsを呼ぶかもしれない
→ PUT /api/customer/emailを呼ぶかもしれない
→ DELETE /api/orders/12345を呼ぶかもしれない
→ 非決定論的、コンテキスト依存
エージェントはランタイムの推論に基づいてどのツールを呼び出すかを決定します。マイクロサービスでうまく機能していた静的RBACポリシーでは対処できません。動的でコンテキストを考慮した認可が必要になるんです。
爆発半径の問題
従来のサービスが侵害された場合、被害はその固定機能に限定されます。でもAIエージェントが侵害(または操作)された場合、爆発半径は付与された権限セット全体に等しくなります:
| 要素 | マイクロサービス | AIエージェント |
|---|---|---|
| アクション | 固定、事前定義 | 動的、モデルが決定 |
| 攻撃ベクトル | コードエクスプロイト | プロンプトインジェクション、ゴールハイジャック |
| 爆発半径 | 単一機能 | 付与された全権限 |
| 監査証跡 | 決定論的ログ | 推論トレースが必要 |
| アクセスパターン | 予測可能 | コンテキスト依存 |
広い権限を持つエージェントは汎用攻撃面と化します。エージェントがアクセスできるすべてのツールは、攻撃者がエージェント経由で呼び出せるツールなんです。
認証情報ライフサイクルの不一致
多くのサービスアカウントは長寿命の認証情報を使っています。四半期ごとにローテーションするAPIキー、有効期限のないサービストークン。決定論的サービスでは(ある程度)許容できます。でもリアルタイムで操作される可能性のあるエージェントではどうでしょう:
// ❌ 多くのチームが現在エージェントをデプロイする方法 const agent = new Agent({ openaiKey: process.env.OPENAI_API_KEY, stripeKey: process.env.STRIPE_SECRET_KEY, // フルアクセス dbConnection: process.env.DATABASE_URL, // 読み取り + 書き込み slackToken: process.env.SLACK_BOT_TOKEN, // 全チャンネル awsCredentials: { accessKeyId: process.env.AWS_ACCESS_KEY, // IAM管理者?? secretAccessKey: process.env.AWS_SECRET_KEY, }, }); // このエージェントが城の鍵を全て握っている
エージェントがプロンプトインジェクションを受けると、これらの認証情報すべてが悪用可能になります。
エージェントアイデンティティモデル
エージェントセキュリティの第一歩は、エージェントを**非人間アイデンティティ(NHI)**として扱うことです。ユーザーアカウントの拡張でも、共有サービスアカウントでもなく、独立したアイデンティティ主体として扱います。
固有のエージェントアイデンティティ
すべてのエージェントインスタンスは、他のエージェントやサービスと認証情報を共有しない固有のアイデンティティを持つべきだ:
interface AgentIdentity { agentId: string; // 一意の識別子 agentType: string; // 例: 'customer-support', 'data-analyst' version: string; // 監査用エージェントバージョン deploymentEnv: string; // 'production' | 'staging' | 'development' owner: string; // 担当チーム createdAt: Date; expiresAt: Date; // 必須の有効期限 maxConcurrentSessions: number; allowedTools: string[]; // 許可ツールのホワイトリスト deniedTools: string[]; // 明示的ブラックリスト } // デプロイ時にエージェントアイデンティティを登録 const identity = await identityProvider.register({ agentType: 'customer-support', version: '2.4.1', owner: 'support-team', expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000), // 24時間 allowedTools: [ 'lookup_order', 'check_shipping_status', 'create_support_ticket', ], deniedTools: [ 'issue_refund', // 人間の承認が必要 'delete_account', // 絶対に自動化しない 'bulk_message', // 絶対に自動化しない ], });
短命でスコープ限定の認証情報
静的APIキーは捨てよう。すべてのエージェントセッションは以下の条件を満たす認証情報を使うべきだ:
- 時間制限: 月単位ではなく、分単位・時間単位で失効
- スコープ制限: 必要な特定ツールへのアクセスのみ許可
- セッション紐付け: エージェントタイプではなく特定セッションに紐付け
class AgentCredentialManager { async getSessionCredentials( agentIdentity: AgentIdentity, sessionContext: SessionContext ): Promise<ScopedCredentials> { // アイデンティティプロバイダーから短命トークンを発行 const token = await this.idp.issueToken({ subject: agentIdentity.agentId, audience: 'tool-gateway', scopes: this.resolveScopes(agentIdentity, sessionContext), expiresIn: '15m', // 15分セッション sessionId: sessionContext.id, constraints: { maxToolCalls: 50, // セッションあたりのハードリミット allowedIPs: ['10.0.0.0/8'], // ネットワーク制限 rateLimit: '100/minute', }, }); return { token, refreshToken: null, // リフレッシュなし、新しいセッションを取得 expiresAt: token.expiresAt, }; } private resolveScopes( identity: AgentIdentity, context: SessionContext ): string[] { // コンテキストに基づく動的スコープ解決 const baseScopes = identity.allowedTools.map( (t) => `tool:${t}:execute` ); // ユーザーティアに応じた権限昇格・制限 if (context.userTier === 'enterprise') { baseScopes.push('tool:priority_support:execute'); } // 時間ベースの制限 const hour = new Date().getHours(); if (hour < 6 || hour > 22) { // 営業時間外: 読み取り専用モード return baseScopes.filter((s) => !s.includes('write')); } return baseScopes; } }
OAuth 2.1でエージェントの委任認可を実装する
AIエージェントがユーザーの代わりに行動する場合、委任認可が必要になります。ユーザーがエージェントに対して限定的かつ時間制限付きのアクセスを明示的に付与する仕組みです。OAuth 2.1はまさにこの目的で設計されました。
エージェント向けOAuthフロー
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ ユーザー │ │エージェント│ │ 認証 │ │リソース │
│ │ │ゲートウェイ│ │ サーバー │ │ サーバー │
└────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │ │
│ "注文の件で │ │ │
│ 手伝って" │ │ │
│──────────────>│ │ │
│ │ │ │
│ 認証が必要 │ │ │
│<──────────────│ │ │
│ │ │ │
│ ログイン + │ │ │
│ スコープ限定 │ │ │
│ の同意 │ │ │
│──────────────────────────────>│ │
│ │ │ │
│ │ スコープ付き │ │
│ │ トークン発行 │ │
│ │<──────────────│ │
│ │ │ │
│ │ トークンで │ │
│ │ API呼び出し │ │
│ │──────────────────────────────>│
│ │ │ │
│ │ レスポンス │ │
│ │<──────────────────────────────│
│ │ │ │
│ エージェント │ │ │
│ が結果を利用 │ │ │
│<──────────────│ │ │
実装: OAuth 2.1 + PKCE
import { AuthorizationCode } from 'simple-oauth2'; class AgentOAuthManager { private client: AuthorizationCode; constructor() { this.client = new AuthorizationCode({ client: { id: process.env.AGENT_CLIENT_ID!, secret: '', // パブリッククライアント(シークレットなし) }, auth: { tokenHost: process.env.AUTH_SERVER_URL!, authorizePath: '/authorize', tokenPath: '/token', }, }); } async initiateUserConsent( userId: string, requiredScopes: string[] ): Promise<ConsentRequest> { // PKCEチャレンジを生成 const codeVerifier = crypto.randomBytes(32) .toString('base64url'); const codeChallenge = crypto .createHash('sha256') .update(codeVerifier) .digest('base64url'); const authorizationUrl = this.client.authorizeURL({ redirect_uri: process.env.AGENT_CALLBACK_URL, scope: requiredScopes.join(' '), state: crypto.randomUUID(), code_challenge: codeChallenge, code_challenge_method: 'S256', }); // トークン交換用にverifierを保存 await this.storeSession(userId, { codeVerifier }); return { consentUrl: authorizationUrl, scopes: requiredScopes, expiresIn: 300, // 5分以内に完了 }; } async exchangeCode( userId: string, authorizationCode: string ): Promise<AgentToken> { const session = await this.getSession(userId); const tokenResponse = await this.client.getToken({ code: authorizationCode, redirect_uri: process.env.AGENT_CALLBACK_URL, code_verifier: session.codeVerifier, }); return { accessToken: tokenResponse.token.access_token, expiresAt: new Date(tokenResponse.token.expires_at), scopes: tokenResponse.token.scope.split(' '), // リフレッシュトークンなし: エージェントは再同意を要求 }; } }
きめ細かなスコープ設計
最小権限の原則を適用できるほど狭いスコープを設計しよう:
// ❌ BAD: 広すぎるスコープ const scopes = ['orders:full', 'customers:full', 'payments:full']; // ✅ GOOD: きめ細かな、アクション別スコープ const scopes = [ 'orders:read', // 注文照会のみ 'orders:status:read', // 配送状況確認のみ 'tickets:create', // サポートチケット作成のみ // 含まれない: // 'orders:write' // 注文変更不可 // 'refunds:create' // 返金発行不可 // 'customers:delete' // アカウント削除不可 ]; // さらに良い方法: リソース別スコープ const scopes = [ 'orders:read:user:usr_abc123', // このユーザーの注文のみ 'tickets:create:org:org_xyz789', // この組織のチケットのみ ];
ツールゲートウェイ: すべてのエージェントアクションを傍受する
最も重要なセキュリティレイヤーは、エージェントと呼び出し可能なすべてのツールの間に位置するものです。エージェントアクションのファイアウォールとして考えましょう。
アーキテクチャ
┌─────────────┐ ┌─────────────────────────────────────┐
│ │ │ ツールゲートウェイ │
│ エージェント│ │ │
│ ランタイム │───>│ ┌──────────┐ ┌───────────────┐ │
│ │ │ │ 認可 │ │ レートリミッ │ │
│ │ │ │ エンジン │ │ ター │ │
└─────────────┘ │ └────┬─────┘ └───────┬───────┘ │
│ │ │ │
│ ┌────▼────────────────▼───────┐ │
│ │ ポリシー適用ポイント(PEP) │ │
│ └────┬─────────────────────────┘ │
│ │ │
│ ┌────▼─────────────────────────┐ │
│ │ 監査ロガー │ │
│ └────┬─────────────────────────┘ │
└───────┼─────────────────────────────┘
│
┌────────────┼────────────────┐
│ │ │
┌─────▼────┐ ┌────▼─────┐ ┌──────▼──────┐
│ Stripe │ │ DB │ │ Slack API │
│ API │ │ │ │ │
└──────────┘ └──────────┘ └─────────────┘
実装
class ToolGateway { async executeToolCall( request: ToolCallRequest ): Promise<ToolCallResult> { // ステップ 1: エージェントのアイデンティティとセッションを検証 const session = await this.verifySession(request); if (!session.valid) { throw new UnauthorizedError('無効または期限切れセッション'); } // ステップ 2: レートリミットの確認 const rateLimitOk = await this.rateLimiter.check( request.agentId, request.toolName ); if (!rateLimitOk) { throw new RateLimitError('ツール呼び出しレートリミット超過'); } // ステップ 3: 認可ポリシーの評価 const decision = await this.authzEngine.evaluate({ subject: request.agentId, action: request.toolName, resource: request.parameters, context: { sessionId: request.sessionId, time: new Date(), reasoning: request.reasoning, }, }); // ステップ 4: ポリシー決定の処理 if (!decision.allowed) { throw new ForbiddenError(decision.reason); } // ステップ 5: 必要に応じて人間の承認 if (decision.requiresApproval) { const approved = await this.requestHumanApproval(request); if (!approved) { throw new ForbiddenError('人間の承認が拒否されました'); } } // ステップ 6: パラメータをサニタイズして実行 const sanitizedParams = decision.modifiedParams || this.sanitizeParams(request.parameters); // ステップ 7: 監査付きで実行 return await this.executeWithAudit( request.toolName, sanitizedParams, request ); } }
MCPを標準ゲートウェイとして活用する
Model Context Protocol(MCP)はエージェント-ツール間通信のデファクトスタンダードになった。MCPサーバーをセキュリティ境界として活用しよう:
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { z } from 'zod'; const server = new McpServer({ name: 'secure-tools', version: '1.0.0', }); // セキュリティ内蔵のツール登録(Zodバリデーション) server.registerTool( 'lookup_order', { description: '注文IDで注文詳細を照会', inputSchema: z.object({ orderId: z.string() .regex(/^ORD-[A-Z0-9]{8}$/) .describe('照会する注文ID'), }), }, async ({ orderId }, { meta }) => { const hasScope = await verifyScope( meta.authToken, 'orders:read' ); if (!hasScope) { return { content: [ { type: 'text', text: 'エラー: 注文照会に必要な権限がありません', }, ], isError: true, }; } const order = await db.orders.findByIdAndUser( orderId, meta.userId ); if (!order) { return { content: [ { type: 'text', text: '注文が見つからないか、アクセスが拒否されました', }, ], isError: true, }; } return { content: [ { type: 'text', text: JSON.stringify(sanitizeOrder(order)), }, ], }; } );
Human-in-the-Loop(HITL)承認フロー
すべてのアクションに人間の承認を求めるのは自動化の意味を失わせます。でも高リスク操作には不可欠なんです。
リスク分類マトリクス
enum RiskLevel { LOW = 'low', // 自動承認 MEDIUM = 'medium', // ログして進行、非同期レビュー HIGH = 'high', // リアルタイム人間承認必須 CRITICAL = 'critical', // 完全ブロック } const TOOL_RISK_CLASSIFICATION: Record<string, RiskLevel> = { // 低リスク: 自動承認 'lookup_order': RiskLevel.LOW, 'check_shipping': RiskLevel.LOW, 'search_faq': RiskLevel.LOW, // 中リスク: 進行するがレビューフラグ 'create_ticket': RiskLevel.MEDIUM, 'update_customer_preferences': RiskLevel.MEDIUM, 'send_notification': RiskLevel.MEDIUM, // 高リスク: リアルタイム承認必須 'issue_refund': RiskLevel.HIGH, 'modify_subscription': RiskLevel.HIGH, 'access_pii': RiskLevel.HIGH, 'escalate_to_human': RiskLevel.HIGH, // 致命的: 自動実行を絶対に許可しない 'delete_account': RiskLevel.CRITICAL, 'bulk_data_export': RiskLevel.CRITICAL, 'modify_permissions': RiskLevel.CRITICAL, 'execute_code': RiskLevel.CRITICAL, };
リアルタイム承認の実装
class ApprovalQueue { async requestApproval( request: ToolCallRequest ): Promise<boolean> { const risk = TOOL_RISK_CLASSIFICATION[request.toolName]; if (risk === RiskLevel.CRITICAL) { return false; // 常に拒否 } if (risk === RiskLevel.LOW) { return true; // 常に許可 } if (risk === RiskLevel.MEDIUM) { // 自動承認するが非同期レビュー用にフラグ await this.flagForReview(request); return true; } // 高リスク: 同期承認必須 const approval = await this.createApprovalRequest({ toolName: request.toolName, parameters: request.parameters, reasoning: request.reasoning, agentId: request.agentId, timeout: 300000, // 5分 }); // Slack/Teams/PagerDutyでレビュアーに通知 await this.notifyReviewers(approval); // 決定を待機 const result = await this.waitForDecision( approval.id, approval.timeout ); // タイムアウト = 拒否 (fail-closed) return result?.approved ?? false; } }
多層防御: レイヤー型セキュリティアーキテクチャ
単一のセキュリティレイヤーでは不十分です。プロダクションエージェントのデプロイには多層防御が必須になります。
レイヤー 1: 入力フィルタリング(エージェント前)
class AgentInputFilter { private readonly INJECTION_PATTERNS = [ /ignore\s+(all\s+)?previous\s+instructions/i, /you\s+are\s+now\s+a/i, /system\s*:\s*/i, /\bact\s+as\b/i, /forget\s+(everything|all|your)/i, /new\s+instructions?\s*:/i, /admin\s+(mode|access|override)/i, ]; async filterInput(input: string): Promise<FilterResult> { for (const pattern of this.INJECTION_PATTERNS) { if (pattern.test(input)) { return { safe: false, reason: `潜在的なインジェクション検出: ${pattern.source}`, sanitized: null, }; } } const classification = await this.classifyIntent(input); if (classification.maliciousScore > 0.7) { return { safe: false, reason: `悪意あるコンテンツとして分類 (スコア: ${classification.maliciousScore})`, sanitized: null, }; } return { safe: true, reason: null, sanitized: input }; } }
レイヤー 2: ツール呼び出しの検証(ゲートウェイ)
上書で既にカバーした通り:AuthZ、レートリミッティング、承認フローを統合したツールゲートウェイ。
レイヤー 3: 出力フィルタリング(エージェント後)
class AgentOutputFilter { async filterOutput( output: string, context: SessionContext ): Promise<FilterResult> { // PII検出とマスキング const piiCheck = await this.detectPII(output); if (piiCheck.found) { output = this.redactPII(output, piiCheck.entities); } // 機密データ漏洩防止 const secrets = this.detectSecretsInOutput(output); if (secrets.length > 0) { output = '[編集済み: 出力に機密データが含まれていました]'; await this.alertSecurityTeam({ type: 'SECRET_LEAK_PREVENTED', context, }); } return { safe: true, reason: null, sanitized: output }; } }
レイヤー 4: 行動異常検出
class AgentBehaviorMonitor { async monitorAction( action: AgentAction ): Promise<AnomalyResult> { const baseline = this.baselines.get(action.agentType); if (!baseline) return { anomalous: false }; const anomalies: string[] = []; // ツール使用頻度の異常 const toolFreq = await this.getToolFrequency( action.agentId, action.toolName, '1h' ); if (toolFreq > baseline.toolFrequency[action.toolName] * 3) { anomalies.push( `"${action.toolName}" 呼び出し${toolFreq}回 (基準: ${baseline.toolFrequency[action.toolName]}回)` ); } // 新しいツール・アクセスパターン const previousTools = await this.getHistoricalTools( action.agentId, '30d' ); if (!previousTools.includes(action.toolName)) { anomalies.push( `初回ツールアクセス: "${action.toolName}"` ); } if (anomalies.length > 0) { await this.triggerAlert({ agentId: action.agentId, anomalies, severity: anomalies.length > 2 ? 'critical' : 'warning', }); } return { anomalous: anomalies.length > 0, details: anomalies, }; } }
監査ログ: フォレンジックの基盤
すべてのエージェントアクションは、意思決定チェーン全体を再構築できる十分なコンテキストとともに不変的にログに記録されなければならない:
interface AgentAuditEntry { // アイデンティティ timestamp: Date; traceId: string; agentId: string; agentType: string; sessionId: string; // アクターコンテキスト triggerUserId: string | null; triggerSource: 'user' | 'schedule' | 'event' | 'agent'; // アクション event: 'TOOL_CALL' | 'TOOL_DENIED' | 'APPROVAL_REQUESTED' | 'APPROVAL_GRANTED' | 'APPROVAL_DENIED' | 'RATE_LIMITED' | 'ANOMALY_DETECTED' | 'SESSION_CREATED' | 'SESSION_EXPIRED'; // 詳細 toolName: string; parameters: Record<string, unknown>; // サニタイズ済み reasoning: string; policyDecision: PolicyDecision; result: 'success' | 'failure' | 'denied' | 'timeout'; // コスト tokensConsumed: number; estimatedCost: number; // メタデータ modelUsed: string; latencyMs: number; } // 重要: エージェントアクションと人間のアクションを区別する class AuditLogger { async log(entry: AgentAuditEntry): Promise<void> { // 追記専用の不変ストア await this.immutableStore.append({ ...entry, actorType: 'AI_AGENT', hash: this.computeHash(entry), // 改ざん検知 }); // モニタリング用リアルタイムストリーミング await this.eventStream.publish('agent.audit', entry); } }
プロダクション・アンチパターン
アンチパターン 1: 共有APIキー
// ❌ 絶対NG: 複数エージェントが1つの認証情報を共有 const agentA = new Agent({ apiKey: SHARED_KEY }); const agentB = new Agent({ apiKey: SHARED_KEY }); // ログでAとBを区別できない // キーを1つ失効させると全エージェントが停止 // ✅ 常に: エージェントごと、セッションごとの認証情報 const agentA = new Agent({ credential: await issueCredential({ agentId: 'agent-a', sessionId: 'sess-123', scopes: ['orders:read'], expiresIn: '15m', }), });
アンチパターン 2: 「ゴッドモード」権限
// ❌ 絶対NG: DB全体にアクセスできるエージェント const agent = new Agent({ db: new PrismaClient(), // スキーマ全体にアクセス tools: ALL_TOOLS, // 全ツール利用可能 }); // ✅ 常に: 最小限のホワイトリストベースアクセス const agent = new Agent({ db: new ReadOnlyClient({ allowedTables: ['orders', 'products'], allowedOperations: ['SELECT'], rowLimit: 100, }), tools: SUPPORT_AGENT_TOOLS, // 厳選されたサブセット });
アンチパターン 3: エージェントの推論を信頼する
// ❌ 絶対NG: エージェントが自ら権限昇格を決定 if (agent.reasoning.includes('管理者アクセスが必要です')) { grantAdminAccess(agent); // エージェントが丁寧にお願いしたからOK! } // ✅ 常に: 権限変更はアウトオブバンド承認必須 // 権限はデプロイ時に定義。ランタイムではない // エージェントは新しい権限を要求も付与もできない
アンチパターン 4: 緊急キルスイッチなし
// ❌ 絶対NG: 侵害されたエージェントを止める手段がない agent.run(); // そして祈る // ✅ 常に: サーキットブレーカー + キルスイッチ const controller = new AbortController(); const breaker = new CircuitBreaker({ maxFailures: 5, maxCost: 100, // 最大$100 timeout: 60000, signal: controller.signal, }); // 外部キルスイッチ adminApi.on('kill-agent', (agentId) => { if (agentId === agent.id) { controller.abort('管理者による緊急シャットダウン'); revokeAllCredentials(agent.id); notifySecurityTeam(agent.id, 'EMERGENCY_KILL'); } });
セキュリティチェックリスト
AIエージェントをプロダクションにデプロイする前に毎回確認:
アイデンティティと認証:
- エージェントが固有アイデンティティを保有(共有認証情報ではない)
- 認証情報が短命(日/月ではなく分/時間)
- 委任ユーザー認可用OAuth 2.1 + PKCE適用
- エージェント設定に静的APIキーなし
- 認証情報ローテーションが自動化済み
認可と権限:
- ツールアクセスがホワイトリストベース(明示的許可、デフォルト拒否)
- スコープがきめ細かくアクション別に特定
- 動的認可がコンテキストを考慮(時間、リスク、ユーザーティア)
- エージェントが自己権限昇格不可
- 高リスク操作にHuman-in-the-Loop承認必須
ツールゲートウェイ:
- すべてのツール呼び出しが集中ゲートウェイを通過
- 入力パラメータが検証・サニタイズ済み
- エージェント別、ツール別レートリミット適用
- MCPまたは同等プロトコルでツール通信を標準化
- 出力がPII・機密データのフィルタリング済み
モニタリングと対応:
- 全エージェントアクションが不変ログに記録
- ログでエージェントアクションと人間のアクションが区別可能
- 行動異常検出がアクティブ
- 全エージェントに緊急キルスイッチが存在
- インシデント対応プレイブックにエージェント侵害シナリオを含む
AIエージェントは2026年で最も強力で、そして最も危険なソフトウェアパターンです。本物の認証情報で本物のシステムに対して自律的に判断を下す。エージェントセキュリティを従来のAPIセキュリティと同じように扱うなら、誤った前提の上に構築していることになります。エージェントには動的認可、スコープ限定認証情報、ゲートウェイベースのツールアクセス制御、高リスクアクションの人間による監督、そして継続的な行動モニタリングが必要です。このガイドのパターンは、エージェントが非決定論的で操作可能なんだという現実、権限が信頼性を超えたときに実害が発生するという現実のために設計されています。実行レイヤーをしっかり守れば、エージェントは生産性を何倍にも引き上げる武器になります。無視すれば、プロンプトインジェクション1回で最悪のインシデントです。
関連ツールを見る
Pockitの無料開発者ツールを試してみましょう