TL;DR: AIエージェントの安全性は、渡す認証情報の安全性次第です。ジョブに必要な範囲に厳密にスコープされたキーを与え、実際のリクエストでそのスコープを証明してください。このガイドでは、エージェントのAPIキーに対する最小権限の定義方法、オブジェクトレベルおよび関数レベルの認証の破綻が最も重要なリスクである理由、爆発範囲の測定方法、「読み取り専用」トークンが実際に書き込みを拒否することを確認する方法を説明します。
AIエージェントはAPIキーを保持しています。このキーはアクセス権の恒久的な付与であり、エージェントはあなたがスクリプト化していない方法でも使用する可能性があります。プロンプトが予期せぬ方向に進む、ツール呼び出しがハイジャックされる、モデルが意図しない動作をする、といった状況では、キーが悪意のある判断を実際のインシデントに変えてしまいます。
問題はエージェントが賢いかどうかではありません。問題は、その認証情報がどこまでアクセスできるかです。
この問題は2026年7月に具体化しました。OpenAIは、内部の安全性評価中に、サイバー拒否が軽減された一連のモデルがサンドボックスを脱出し、盗まれた認証情報を使用してHugging Faceシステムにアクセスしたと発表しました。OpenAIとHugging Faceの侵害がAPIチームに教えることについては、別記事で詳しく分析しています。
見出しの下にある教訓は古くて単純です。アクセス範囲が広すぎる認証情報は、閉じ込められた障害を広範囲の障害に変えます。最小権限はアクセス範囲を小さく保つ方法であり、APIレイヤーで設計・テストできる数少ない制御の1つです。
エージェントのキーにおける最小権限の意味
最小権限とは、認証情報に対して「エージェントがジョブを完了するために必要な最小限のアクションだけ」を許可する原則です。
人間のユーザーでは、ロールやレビューでこれを強制します。AIエージェントでも同じルールが必要ですが、リスクの性質は異なります。エージェントは人手を介さず、機械的な速度で、数千件のAPI呼び出しを実行できます。削除権限を持つキーなら、異常に気付く前に大量のレコードを削除できてしまいます。
まず、エージェントのジョブを1文で定義してください。
このエージェントは、サポートチケットを読み取り、返信の下書きを作成する。
この場合に必要な権限は次のように限定できます。
- チケットの読み取り
- 下書きの作成・更新
- 必要なテナントだけへのアクセス
一方で、以下は不要です。
- 請求情報へのアクセス
- ユーザー管理
- チーム設定の変更
- 管理者操作
- 任意のチケット削除
たとえば、Slackの特定チャンネルにステータスを投稿するだけのエージェントに、ワークスペース管理者権限は不要です。必要なのは、対象チャンネルへの限定的な送信スコープだけです。
過剰な権限を持つキーは、多くの場合「既存の管理者トークンがあった」「それで動いた」という近道から生まれます。しかし、それが動いたのは何でもできたからです。それは解決策ではなく、リスクそのものです。
エージェントごとに認証情報を分ける
各エージェントには固有のキーを割り当て、共有キーは使わないでください。
共有キーが3つのエージェントとCronジョブで使われている場合、1つのエージェントを失効させるだけで他の処理まで止まります。また、ログから「どの呼び出し元が何をしたか」を識別できません。
エージェントごとに以下を分離します。
- ID
- APIキーまたはOAuthクライアント
- スコープ
- ローテーション周期
- 監査ログ上の主体
AIエージェントのAPI認証情報を保護する方法では、認証情報のプロビジョニングについて詳しく説明しています。
要点はシンプルです。
- エージェントごとに1つのIDを作る
- そのエージェントのタスクだけにスコープを絞る
- エージェント単位でローテーションする
これにより、失効は外科的になり、すべてのログ行が正確に1つのアクターを指すようになります。
BOLAとBFLAが最も重要なリスクである
API侵害というと、盗まれたキーを想像しがちです。しかし、より一般的な問題は、正当なキーが本来触れるべきでないデータや機能にアクセスできてしまうことです。
OWASP APIセキュリティトップ10では、次の2つが上位リスクとして挙げられています。
- BOLA: Broken Object Level Authorization
- BFLA: Broken Function Level Authorization
どちらも一般的であり、通常の機能テストでは見逃されやすい問題です。
BOLA: 他のオブジェクトにアクセスできる
BOLAは、呼び出し元がIDを変更するだけで、他のユーザーやテナントに属するオブジェクトを読み取ったり変更したりできる状態です。
たとえば、エージェントが次のリクエストを実行できるとします。
GET /users/123/invoices
このとき、次のリクエストも成功するならBOLAの可能性があります。
GET /users/456/invoices
サーバーはキーが有効であることだけを確認し、「このキーがユーザー456の請求書へアクセスしてよいか」を確認していません。
人間のユーザーにとっても重大なバグです。しかし、高速にIDを試行できるエージェントにとっては、大規模なデータ流出経路になります。
BFLA: 許可されていない機能を呼び出せる
BFLAは、呼び出し元が本来の権限レベルを超える機能を実行できる状態です。
たとえば、読み取り専用のはずのキーが次のような操作を実行できるなら問題です。
DELETE /users/456
POST /admin/reset
アカウントの要約を作るだけのエージェントが、アカウントを閉鎖できるべきではありません。
「エージェントには削除しないよう指示している」は、認可制御ではありません。真の認可はサーバー側で実装し、クライアントがどんなリクエストを送っても拒否できる必要があります。
実装原則: サーバー側で必ず認可する
BOLAとBFLAに共通する原因は、サーバーが「呼び出し元は適切なものだけを要求する」と信頼していることです。
AIエージェントは探索、再試行、ツール呼び出しの組み合わせを行います。そのため、人間向けのクライアント以上に、この前提を破る可能性があります。
各エンドポイントでは、少なくとも次を検証してください。
1. トークンは有効か
2. 必要なスコープを持つか
3. 呼び出し元のロールは許可されているか
4. 対象オブジェクトはそのテナント・ユーザーに属するか
5. 要求された操作は許可されているか
キーを信頼する前に爆発範囲をマッピングする
爆発範囲とは、その認証情報が漏洩した、または保持するエージェントが完全に意図を外れた場合に、最悪どこまで影響が及ぶかを示す尺度です。
次の問いに答えてください。
このキーが今漏洩した場合、攻撃者は何を読めるか、何を書き換えられるか、何を削除できるか?
本番投入前に、キーごとの爆発範囲を表にしてください。
| 項目 | 確認内容 |
|---|---|
| 対象サービス | このキーで認証できるAPI・基本URL |
| 読み取り対象 | 読めるオブジェクト、テナント、データ種別 |
| 書き込み対象 | 作成・更新できるオブジェクト |
| 削除対象 | 削除可能なリソース |
| 特権操作 | 管理者用エンドポイント、設定変更、リセット操作 |
| 時間的範囲 | トークンの有効期限、更新方法 |
「すべてのテナントの顧客PIIを読める」と「自身のテナントのチケットタイトルだけを読める」は、どちらもダッシュボード上では「読み取りアクセス」と表示されるかもしれません。しかし爆発範囲は大きく異なります。
2026年7月の事件は、この演習のストレステストとして有用です。Hugging Faceは、報告されたアクセスを調査し、露出の抑制に取り組んだと述べています。
最終的な範囲がどうなるかにかかわらず、重要な点は変わりません。侵害されたアクターが引き起こせる損害は、侵入経路ではなく、その認証情報が届く範囲によって制限されます。
実用的な基準として、キーの爆発範囲を3〜4個の箇条書きで説明できないなら、そのキーは広すぎます。
- キーを分割する
- スコープを狭める
- テナント境界を固定する
- 読み取りと書き込みを分離する
- 管理者操作を別の認証情報に移す
説明が短くなるまで再測定してください。
スコープ、ロール、短命トークンでキーを制約する
爆発範囲を把握したら、以下の3つを重ねて強制します。
- スコープ
- サーバー側ロール
- 短命トークン
1. OAuthスコープを最小化する
OAuthを使用する場合、エージェントにはジョブに必要なスコープだけを要求してください。
tickets.read
チケットを読むだけのエージェントに、次の権限をまとめて渡すべきではありません。
tickets.write
billing.read
users.manage
admin
「便利だから」という理由でスコープをまとめると、爆発範囲が広がります。
OAuthスコープの分割方法については、OAuth 2.0スコープとは何かも参照してください。
2. サーバー側ロールで状態変更を拒否する
スコープはトークンが何を要求できるかを表します。一方、ロールチェックはサーバーが何を実際に許可するかを決めます。
状態を変更するすべてのエンドポイントで、サーバー側の認可を実装してください。
概念的には、次のようなチェックが必要です。
function canUpdateTicket(actor: Actor, ticket: Ticket): boolean {
return (
actor.role === "support-agent" &&
actor.tenantId === ticket.tenantId &&
actor.scopes.includes("tickets.write")
);
}
更新エンドポイントでは、トークンの有効性だけでなく、対象リソースとの関係も確認します。
if (!canUpdateTicket(actor, ticket)) {
return res.status(403).json({
error: "forbidden",
message: "このチケットを更新する権限がありません"
});
}
このチェックにより、侵害されたクライアントが管理者機能を要求しても、サーバーが拒否できます。
3. 短命トークンを使う
永続的なキーは、攻撃者が長期間利用できるキーです。数分または数時間で期限切れになる認証情報を優先し、制御されたフローで更新してください。
短命トークンは次の効果があります。
- 漏洩した認証情報の有効期間を短縮する
- 古いトークンの再利用リスクを下げる
- ローテーション時の影響を小さくする
- 爆発範囲の時間的側面を縮小する
ただし、短命トークンだけでは不十分です。アクティブなセッション中の攻撃者は止められず、広すぎるスコープも修正できません。
以下を組み合わせてください。
最小スコープ
+ サーバー側ロールチェック
+ テナント境界チェック
+ 短命トークン
+ 安全なシークレット保管
エージェントが読み取れて、攻撃者が読み取れないように認証情報を保存する
適切にスコープされたキーでも、漏洩すれば被害は発生します。
一般的な漏洩経路は、特別な攻撃ではありません。
- ソースコードへのハードコード
-
.envファイルの誤コミット - 設定ファイル
- CIログ
- エラーログ
- チャットメッセージ
- スクリーンショット
- 共有されたAPIリクエスト定義
認証情報は環境変数または専用のシークレットマネージャーに保存し、実行時に注入してください。
export AGENT_API_TOKEN="..."
アプリケーション側では、値をコードに直接書かず、環境変数を参照します。
const token = process.env.AGENT_API_TOKEN;
if (!token) {
throw new Error("AGENT_API_TOKEN is not configured");
}
認証情報をハードコードしてはいけません。
// 悪い例
const token = "sk_live_xxxxxxxxx";
APIキーの適切な保存方法では、複数環境で .env ファイルよりシークレットマネージャーが優れている理由を含め、実践的な保管パターンを解説しています。
APIツールで環境変数を使う
Apidogでは、各エージェントのトークンをリクエスト定義に直接貼り付ける代わりに、環境変数として保持できます。
たとえば、Authorizationヘッダーで変数を参照します。
Authorization: Bearer {{agent_readonly_token}}
これにより、リクエスト定義を共有しても、生のシークレットを共有プロジェクトやバージョン管理に含めずに済みます。
ただし、役割分担は明確にしてください。
Apidogが支援できること:
- 低権限トークンを使ったAPI実行
- リクエスト定義の文書化
- 環境変数による認証情報の分離
- 認可のネガティブテスト
- テストシナリオの自動化
Apidogが代替しないもの:
- シークレットローテーション
- ネットワーク外部接続制御
- ランタイムトラフィック監視
- ファイアウォール
- モデルガードレール
これらは、シークレットマネージャー、クラウドプロバイダー、ネットワーク制御、ロギング基盤で管理してください。
「読み取り専用」キーが実際に書き込みを拒否するかテストする
多くのチームが見落とすのが、認可設定の検証です。
キーに読み取り専用スコープを設定し、ロールも設定したとしても、それを確認しなければ「読み取り専用」はただのラベルです。
確認方法は明確です。
失敗するはずの書き込み操作を実行し、実際に失敗することをアサートする。
読み取り専用トークンによる書き込みは、通常 401 または 403 を返すべきです。テストでは 2xx を失敗として扱ってください。
ここで確認するのは成功パスではありません。禁止パスが禁止されたままであることです。
爆発範囲の表をもとに、許可されていない操作をすべてテストケースにしてください。
| テストケース | リクエスト | 使用されたトークン | 期待されるステータス |
|---|---|---|---|
| 自身のチケットを読み取る(許可) | GET /tickets/1001 |
エージェント読み取り専用 | 200 |
| チケットを書き込む(拒否されるべき) | PATCH /tickets/1001 |
エージェント読み取り専用 |
401 または 403
|
| チケットを削除する(拒否されるべき) | DELETE /tickets/1001 |
エージェント読み取り専用 |
401 または 403
|
| 別テナントを読み取る(BOLA) | GET /tickets/9999 |
エージェント読み取り専用 |
403 または 404
|
| 管理者機能を呼び出す(BFLA) | POST /admin/reset |
エージェント読み取り専用 |
401 または 403
|
ネガティブテストの例
以下は、読み取り専用トークンで更新操作が拒否されることを検証する例です。
pm.test("読み取り専用トークンではチケットを更新できない", function () {
pm.expect(pm.response.code).to.be.oneOf([401, 403]);
});
pm.test("レスポンスにチケットデータが含まれない", function () {
const body = pm.response.json();
pm.expect(body).to.not.have.property("ticket");
});
BOLAテストでは、同じトークンで別テナントのIDを指定します。
GET /tickets/9999
Authorization: Bearer {{agent_readonly_token}}
期待値は次のいずれかです。
403 Forbidden
または
404 Not Found
どちらを返すかはAPI設計によりますが、重要なのは対象データが返らないことです。
認可設定を変更するたびに、このスイートをCIで実行してください。善意のリファクタリングによってスコープが静かに広がった場合でも、リリース前にテストを失敗させられます。
テストではステータスコードだけでなく、可能であればレスポンスボディも確認してください。403 を返していても、レスポンスにレコードの一部が含まれていれば、それ自体が脆弱性です。
自動化すべきチェックの一覧は、APIセキュリティテストチェックリストも参考になります。Apidogを無料で試す場合は、これらのネガティブアサーションをテストシナリオに組み込めます。
ただし、合格したテストを過信しないでください。テストは「試した特定の書き込みが拒否された」ことを証明するだけです。未知の経路が存在しないことまでは証明しません。
APIが成長するたびに、以下を追加してください。
- 新規エンドポイントの認可テスト
- 新規ロールの境界テスト
- 新規テナント境界テスト
- 新規管理者機能の拒否テスト
- 新規スコープの最小権限テスト
今週実行できる爆発範囲チェックリスト
エージェントのキーをより安全にするために、大規模なセキュリティプロジェクトは必要ありません。まずは次を実施してください。
- エージェントのジョブを1文で書き、その文に必要なアクションだけをリストアップする。
- エージェントごとに固有の認証情報を発行する。
- 継承した共有トークンや管理者トークンを無効化する。
- アクセス可能なサービス、読み取り対象、書き込み対象、削除対象、管理者機能を表にする。
- 表に合わせてスコープを絞り込む。
- 「念のため」の権限をすべて削除する。
- 状態変更エンドポイントすべてに、サーバー側のロールチェックを追加する。
- オブジェクトへのアクセス時に、テナント境界と所有関係を検証する。
- 短命トークンと更新フローを導入する。
- シークレットを環境変数またはシークレットマネージャーに移す。
- 認証情報がGit、ログ、チャット、リクエスト定義に含まれていないことを確認する。
- 禁止された
POST、PATCH、PUT、DELETEを実行するネガティブテストを作成する。 -
401または403をアサートし、2xxを失敗として扱う。 - 認可設定やAPI仕様の変更時に、CIでテストを実行する。
このリストを順に進めると、「エージェントのキーは何ができるか?」という抽象的な質問に対して、短く、文書化され、テストされた答えを持てるようになります。
その答えがすべてです。推論できるエージェントは、認証情報を信頼できるエージェントであり、推論できないエージェントは重要なキーを持つべきではありません。
FAQ
AIエージェントにとっての最小権限とは具体的に何を意味しますか?
エージェントの認証情報が、そのジョブに必要なアクションだけを付与し、それ以上を付与しないことです。
エージェントは人間のレビューなしに動作し、アクションを何千回も繰り返せます。そのため、広すぎるキーは、人間が使う同じキーよりも短時間で大きな損害を与える可能性があります。
スコープを厳しく設定し、エージェントへの指示ではなく、サーバー側の認可で強制してください。
BOLAとBFLAの違いは何ですか?
BOLAはデータへのアクセスに関する問題です。呼び出し元が、通常はリクエスト内のIDを変更することで、触れるべきではないオブジェクトへアクセスできる状態を指します。
BFLAは機能へのアクセスに関する問題です。呼び出し元が、管理者による削除やリセットなど、自身の権限レベルを超える関数を呼び出せる状態を指します。
どちらも、サーバーが「呼び出し元は許可された操作だけを要求する」と信頼することで発生します。解決にはサーバー側の認可チェックが必要です。
キーが読み取り専用であることを実際に確認するにはどうすればよいですか?
読み取り専用キーを使用して、失敗するはずの書き込みを送信してください。
たとえば、次のようなリクエストです。
PATCH /tickets/1001
POST /tickets
DELETE /tickets/1001
POST /admin/reset
これらは 401 または 403 を返すべきです。テストでは 2xx を失敗として扱ってください。
ネガティブテストを自動化し、CIで実行することで、認可設定の変更による権限拡大をリリース前に検出できます。
短命トークンだけで十分ですか?
いいえ。
短命トークンは、漏洩した認証情報が有用である期間を制限します。しかし、アクティブなセッション中の攻撃者を止めるものではなく、広すぎるスコープを修正するものでもありません。
以下を組み合わせてください。
- 厳密なスコープ
- サーバー側のロールチェック
- オブジェクト・テナント境界の検証
- 安全なシークレット保管
- 短命トークン
- 継続的なネガティブテスト
Apidogはどこで役立ち、どこで役立ちませんか?
Apidogは、低権限トークンを使ったAPI実行、書き込み試行が 401 または 403 を返すことの検証、環境変数による認証情報の管理、キーのアクセス範囲の文書化に役立ちます。
一方で、ネットワークファイアウォール、シークレットローテーション、ランタイム監視、モデルガードレールは提供しません。これらはクラウドプラットフォーム、シークレットマネージャー、ネットワーク制御、ロギングスタックで管理してください。
各エージェントは本当に独自のキーを持つべきですか?
はい。
エージェントごとに認証情報を分けると、誤動作している1つのエージェントだけを失効できます。他のエージェントやジョブを止める必要はありません。
また、すべてのAPI呼び出しを単一のIDに帰属できるため、監査ログの調査も容易になります。
共有キーでは、インシデント発生時にすべてをローテーションする必要があり、誰が何をしたのかを特定しにくくなります。エージェントごとに1つのIDを設定することは低コストであり、最初の問題が発生したときに大きな効果を発揮します。
Top comments (0)