LLMアプリのAPIキーをどう守る?ブラウザへ置かず最小権限で回す
LLM APIのキーをフロントエンドへ埋め込むと、画面上で文字を隠しても利用者へ配布される。開発者ツール、通信、ビルド済みJavaScript、モバイルアプリの解析から取り出され、第三者が自分の費用と権限でAPIを呼べる。
基本構成は、ブラウザやモバイルアプリから自分のバックエンドへ要求を送り、バックエンドだけがモデル提供者の認証情報を使う形である。ただし、中継サーバーを置くだけでは安全にならない。利用者認証、権限、利用量制御、キーの保管、ローテーション、漏えい時の失効までを一つの運用として設計する。
APIキーは公開用の識別子ではない
APIキーは、要求を送ったプログラムに特定のプロジェクトや組織の権限を与える秘密情報である。URLに含める公開識別子や、アプリ名を示すラベルとは役割が違う。
キーが漏れると、第三者が次を行える可能性がある。
- モデルAPIを呼び出して費用とレート枠を消費する
- 付与された権限でファイルやジョブなどの資源へアクセスする
- 不適切な利用を正規のアプリからの要求に見せる
- 本番サービスを支出上限やレート制限へ到達させる
キー文字列そのものに利用者ごとの権限判定を任せない。バックエンドで利用者を認証し、どの機能をどれだけ使えるかをアプリ側で決める。
ブラウザとモバイルアプリへ長期キーを置かない
OpenAIのAPI key safety資料は、ブラウザやモバイルアプリのようなクライアント側環境へAPIキーを配置せず、自分のバックエンド経由で要求するよう案内している。
次の場所も公開されたクライアント側と考える。
- JavaScriptのソースとビルド成果物
- HTMLの属性、コメント、設定JSON
- ソースマップ
- デスクトップやモバイルアプリのパッケージ
- ブラウザのLocal Storage、IndexedDB、Cookie
- クライアントから送る独自ヘッダー
- 公開リポジトリと配布用サンプル
難読化、文字列分割、Base64変換、暗号化したキーと復号鍵の同梱は保護にならない。クライアントが自力で使える秘密は、解析した利用者も使える。
CORSとReferer制限はキーを守らない
CORSは、ブラウザ上のJavaScriptが別のオリジンの応答を読めるかを制御する仕組みであり、盗まれたキーをサーバーやスクリプトから使う攻撃を止めない。
RefererやUser-Agentも、要求元の確実な本人確認にはならない。正規画面の通信だけを許可したつもりでも、攻撃者はバックエンドの公開エンドポイントを直接呼べる。
バックエンドには、利用者セッションまたは用途に合う認証、CSRF対策、入力上限、一利用者当たりのレート制限、費用上限を置く。未ログイン利用を許可する場合も、匿名セッション、IPだけに依存しない濫用対策、全体上限を組み合わせる。
バックエンド中継は自由なプロキシにしない
モデル名、システム指示、道具定義、出力上限、任意URLをクライアントからそのまま受け取り、APIへ転送するだけの中継は、キーを隠していても権限を公開しているのと近い。
サーバー側で機能ごとの許可リストを持つ。
- 利用できるモデルと処理方式
- 最大の入力、出力、ファイルサイズ
- 許可する道具と引数Schema
- 検索できるデータ範囲
- 一回と一定期間の費用
- タイムアウトと再試行回数
- 利用者の権限と承認が必要な操作
クライアントは「問い合わせ回答を開始する」など業務上の要求を送り、提供者APIの全パラメータを自由に操作しない。入力を検証し、危険な道具はアプリ側で許可を再確認する。
人のキーではなくワークロードの身元を分ける
開発者個人のAPIキーを本番サーバーへ置くと、退職、異動、権限変更、個人環境の事故が本番へ影響する。実行環境ごとに専用のサービスアカウントやワークロード用資格情報を使う。
少なくとも次を分ける。
- 開発、テスト、ステージング、本番
- 別のアプリまたは重大度の異なる機能
- オンライン処理と大量バッチ
- 管理APIと通常の推論API
- 人が使う認証情報と自動処理の認証情報
OpenAIのRBAC資料も、実験、ステージング、本番を別プロジェクトに分け、サービスアカウントとAPIキーへ必要な権限だけを与える方法を示している。
分離すれば、一つのキーを失効しても全アプリを止めず、利用量と事故範囲をプロジェクト単位で調べられる。
最小権限はAPIキーの範囲とアプリの権限で重ねる
キーには、実行に必要な操作だけを許可する。回答生成だけを行うサービスへ、組織管理、キー作成、ファイル削除などの権限を与えない。
ただし、提供者側の権限を狭めても、アプリ内の顧客データ境界は自動で守られない。利用者Aが利用者Bの会話ID、ファイルID、ベクトルストアIDを指定できないよう、すべての資源をアプリ側の所有者と照合する。
モデルが出した道具引数も利用者入力と同じく信頼しない。SQL、ファイルパス、送信先、顧客IDを検証し、実行主体の権限を毎回確認する。
長期キーより短期資格情報を選べる場合がある
長期間有効な固定キーは扱いやすいが、漏えい後も失効するまで使われ続ける。対応する環境では、ワークロードの身元を信頼されたID基盤で確認し、短期のアクセストークンへ交換する方式を検討する。
OpenAIの現行資料は、対応するワークロードでWorkload Identity Federationを使い、長期APIキーを保存せず、専用サービスアカウントの短期トークンを取得する方法を案内している。
短期化しても権限が広すぎれば危険は残る。発行元の身元、対象プロジェクト、許可する操作、有効期限を狭める。トークン取得権限そのものを守り、監査する。
環境変数は保存場所ではなく受け渡し方法である
ソースコードへキーを直書きするより、実行時に環境変数から読む方が安全である。しかし、環境変数へ入れれば秘密管理が完了するわけではない。
値が次へ漏れる可能性を確認する。
- CIの設定と実行ログ
- コンテナ定義、デバッグ画面、プロセス情報
- 例外レポートとメモリダンプ
- シェル履歴
- 開発者端末の設定ファイル
- サポート用の診断出力
本番では、Secrets ManagerやVaultなど承認された秘密管理サービスに保存し、実行時だけ必要なワークロードへ渡す。保存時と通信時の暗号化に加えて、誰が読み取り、変更し、失効できるかを管理する。
エージェントが生成したコードや任意コマンドを実行する環境へキーを注入すると、そのコードから読める。OpenAIのSandbox security資料も、アプリ用のキーを実行環境の外へ置き、承認された宛先へ資格情報を付加するプロキシやVault secretsを使うよう案内している。
ログとトレースからキーを除く
認証ヘッダー、環境変数、設定全文、curlコマンド、例外オブジェクトをそのままログへ出さない。URLのクエリへ秘密を入れると、アクセスログや解析サービスへ広がる。
ログにはキーの値ではなく、秘密管理側の版ID、末尾の安全な識別子、サービスアカウントID、プロジェクトIDを残す。調査に必要なのは、どの資格情報を使ったかであり、秘密文字列そのものではない。
SDKやHTTPライブラリのデバッグログを有効にする前に、ヘッダーと本文の扱いを確認する。マスキングがあっても、独自ヘッダー、道具引数、ファイルURLまで安全とは限らない。
キーらしい文字列を検出する秘密情報スキャンを、コミット前、CI、コンテナや配布成果物へ適用する。ただし検出できなかったことを安全の証明にしない。
privateリポジトリにもコミットしない
非公開リポジトリでも、共同作業者、CI、バックアップ、外部連携、アカウント侵害から値が広がる。履歴からファイルを消しても、過去のコミット、fork、clone、キャッシュには残る。
誤ってコミットしたら、先にキーを失効し、新しいキーを発行する。履歴を書き換えただけで、漏えいしていないと判断しない。その後で履歴、ビルド成果物、CIログ、配布物から除去する。
サンプル設定には OPENAI_API_KEY=replace_me のような明らかなプレースホルダーだけを置く。実在する形式の架空キーでも、自動スキャンや利用者の混乱を招く場合がある。
ローテーションは無停止で試す
定期的なローテーションを手順書だけにせず、本番を止めずに交換できる構成にする。
- 新しいキーを必要最小権限と有効期限で作る
- 秘密管理サービスへ新しい版として登録する
- 一部のインスタンスまたはステージングで疎通する
- 本番の読み取り先を新しい版へ切り替える
- エラー、利用量、プロジェクトを確認する
- 古いキーを失効する
- キャッシュや長時間プロセスに残っていないか確認する
新旧を同時に有効にする時間を短くする。一つの環境変数を書き換えただけで、すべてのワーカー、バッチ、サーバーレス関数が更新されたとは限らない。
有効期限前の警告と所有者を決める。期限切れで初めて交換手順を試すと、本番障害になりやすい。
ネットワーク制限は追加の層として使う
固定した送信元がある場合は、IP allowlistで認証情報を使えるネットワークを限定できる。mTLSやPrivate Linkに対応する契約と構成なら、通信相手と経路をさらに制約できる。
これらはAPIキー管理の代わりではない。OpenAIのIP allowlist資料も、許可リストは安全なキー保管、ローテーション、アカウント保護を置き換えないと明記している。
本番、ステージング、CI、災害復旧の送信元を確認せずに制限を有効にすると、正規の処理を止める。変更の伝播時間と復旧手順を試す。
漏えいを費用増加より早く検知する
月末の請求で初めて気付くのでは遅い。キー、サービスアカウント、プロジェクトごとに利用量を分け、通常と異なる変化を監視する。
- 未使用時間帯の要求
- 想定外のモデルやAPI
- 急なトークン量と失敗率の増加
- 通常と異なる送信元
- 新しいファイル、Batch、資源の作成
- 支出警告とレート制限
支出上限は被害を抑える一層になるが、反映は瞬時とは限らず、正規の本番要求も止める。アプリ側の利用者別上限、提供者側の権限、ネットワーク制限、監視を組み合わせる。
漏えい時は削除より先に失効する
漏えいが疑われたら、証明を待たず対象キーを失効または無効化する。新しい資格情報へ切り替え、影響範囲を調べる。
確認項目は次である。
- 漏れたキー、権限、所属プロジェクト
- 最初と最後に不正利用された可能性のある時刻
- 利用されたAPI、モデル、ファイル、ジョブ
- 費用、レート枠、作成または変更された資源
- ソース、CI、ログ、端末、第三者サービスへの複製
- 同じ保管経路にある他の秘密情報
ログからキー文字列を消すだけでは、取得済みの第三者を止められない。関連するキーを使い回している場合は、すべての利用先を交換する。
復旧後に、漏えい経路、検知までの時間、失効までの時間を記録し、秘密情報スキャン、権限、ローテーション、警告を更新する。
公開前の確認項目
最低限、次を確かめる。
- クライアント成果物とソースマップにキーがない
- リポジトリ履歴とCIログにキーがない
- 本番は専用のプロジェクトとワークロード資格情報を使う
- キーの権限と有効期限が必要最小限である
- 秘密管理サービスから実行時に取得する
- バックエンドで利用者認証、入力検証、利用量制御を行う
- キーや認証ヘッダーをログへ出さない
- 利用量と支出の警告がキーまたはプロジェクト単位で届く
- 無停止ローテーションと緊急失効を試した
- 漏えい時の担当者と手順が決まっている
LLMアプリのAPIキー保護は、文字列を環境変数へ移すだけでは完了しない。クライアントへ配布せず、バックエンドで用途を限定し、ワークロードごとに最小権限の資格情報を使い、保存、監視、交換、失効まで管理する。キーが漏れる前提で影響範囲を小さくし、漏れたときに短時間で止められる構成が本番運用の基準になる。