はじめに
Qiitaトレンドに挙がった「Google APIキー漏洩で13時間約900万円請求」の記事を見て、よくこんな説明を目にします。
「APIキーの制限設定が甘かったのが原因」
確かにこれは事実の一部ですが、本質を捉えていません。多くのアプリ開発者が陥る罠は、「設定」で解決しようとして、「設計」の脆さを見逃すことにあります。
この記事では、
- Firebase AI Logicの構造
- クラウドの責任境界
- APIキーという仕組みの限界
という仕組みそのものの限界を整理し、「APIキーを守る設計」から「APIキーに依存しない設計」への転換を提案します。
1. Firebase AI Logicの構造を理解する
通常、クライアントにAPIキーを置くのは自殺行為ですが、Firebase AI Logicは以下の構造でそれを回避しようとします。
アーキテクチャ
[クライアント]
↓
[Googleプロキシ]
↓
[Gemini API]
このプロキシが提供するもの
- キーの隠蔽: 生のAPIキーをクライアントコードに埋め込む必要がなくなる。
- App Check連携: 「正規のアプリからのリクエストか」をデバイスレベルで検証する。
- Firebase統合: 認証やストレージとのシームレスな連携。
重要な理解
ここで誤解してはならないのは、このプロキシはセキュリティを「保証」する層ではなく、あくまで「補助(緩和)」する層だということです。
構造上、リクエストを生成する主体は依然として「信頼できないクライアント」にあります。App Checkなどの技術は、リクエストを「信頼」するためのものではなく、「明らかに怪しいものを弾く(消去法)」 ためのフィルターに過ぎません。
構造から見た限界
この構成のポイントはシンプルです。
- リクエスト生成主体はクライアント
- クライアントは信頼できない
結論
- 完全な制御は構造上不可能
2. クラウドの責任境界(GCP)
「GCPやFirebaseを使っているから、Googleがある程度守ってくれるだろう」という期待は、責任境界の誤解から生まれます。
ここでクラウドの前提を整理します。
対象:Google Cloud Platform
| 領域 | 担当 | 備考 |
|---|---|---|
| 物理インフラ | サーバーの堅牢性、可用性など | |
| 認証・認可の設計 | ユーザー(開発者) | どの方式で認証するかを決める責任 |
| APIキーの管理 | ユーザー(開発者) | キーの露出防止、制限設定 |
| 利用量の制御 | ユーザー(開発者) | レート制限、クォータ監視 |
よくある誤解
「クラウドが守ってくれる」
実際
クラウドは「安全なサービス」を提供しているのではなく、「安全に構成するための手段」 を提供しているに過ぎません。道具をどう組み合わせ、どこに責任の線を引くかは、すべて開発者の「設計」に委ねられています。
3. APIキーの本質:認証ではなく“所持証明”
この記事の核心です。なぜ「設定」だけでは不十分なのか。それはAPIキーの性質にあります。
APIキーは典型的なBearerトークン(持参人トークン) です。
- 持っている人が使える: 誰が使っているかの識別(Identity)が困難。
- 権限の分離が困難: キーが漏れれば、設定されたクォータの範囲内で全権限が行使される。
- 「APIキーを発行し、それを通信経路に乗せた時点で、構造的な脆弱性を抱えている」
この認識を持つことが、強固な設計への第一歩です。
4. なぜ対策が効ききらないのか
一般に推奨される対策を、あえて「否定的な視点」で再評価してみます。
- API制限: 「Geminiしか叩けないキー」に絞っても、そのGeminiで数万ドルの請求を立てられれば終わりです。
- IP制限: プロキシ経由やボットネットによる分散攻撃の前には無力です。
- App Check: 強力な検問ですが、正規デバイスを介したリプレイ攻撃を完全には防げません。
- APIキーのローテーション: 短時間(数時間)で数万ドルを消費する「爆発型」の攻撃には、事後対策でしかありません。
これらはすべて「防御(Defense)」ではなく、「被害軽減(Mitigation)」 です。
5. 設計のゴール:APIキーを排除する
真に安全な設計とは、「APIキーという概念をクライアントから消し去ること」 です。
推奨アーキテクチャ
クライアント(Web / モバイル)
↓ HTTPS
バックエンド(Cloud Run)
↓ IAM(サービスアカウント)
Gemini / Vertex AI
使用技術例
- Cloud Run:APIサーバー
- Vertex AI:Gemini呼び出し(IAM対応)
実装ステップ(最短ルート)
① サービスアカウントを用意
- 最小権限でOK(例:Vertex AIの呼び出し権限)
- 環境ごとに分離(dev / prod)
② Cloud Runに紐づけ - 実行サービスアカウントに上記を指定
- コード側で鍵ファイルは不要(メタデータ経由で自動取得)
③ バックエンドAPIを作る
例(Node.js・概念)
// 擬似コード
import {GoogleAuth} from "google-auth-library";
const auth = new GoogleAuth({
scopes: "https://www.googleapis.com/auth/cloud-platform",
});
export async function handler(req, res) {
const client = await auth.getClient();
const response = await client.request({
url: "https://vertexai.googleapis.com/v1/projects/PROJECT/locations/...:generateContent",
method: "POST",
data: {
contents: [{ role: "user", parts: [{ text: req.body.prompt }] }]
}
});
res.json(response.data);
}
ポイント
- Bearerトークンはライブラリが自動付与
- トークンはキャッシュされるのでオーバーヘッドは小さい
クライアントはバックエンドだけ叩く
await fetch("/api/generate", {
method: "POST",
body: JSON.stringify({ prompt })
});
ポイント
- Geminiに直接触れない
なぜこの設計が安全か
- APIキーが存在しない: 通信経路にキーが流れないため、盗まれる実体がありません。
- IAM(Identity and Access Management): サーバー間認証により、短命でセキュアなアクセストークンを自動利用します。
- 完全な制御: バックエンドで「誰が」「1分間に何回」実行できるかを、Redis等を用いて厳密に制御できます。
Firebaseを残す構成
Firebaseは使い続けますがFirebase AI Logicを捨てる構成です。
アーキテクチャー
Firebase(Auth / App Check)
↓
Cloud Run(本体ロジック)
↓
Gemini(IAM)
APIキーを排除した設計の具体例として、FirebaseとCloud Runを組み合わせた構成は、実務において最も「自然」かつ「拡張性が高い」選択肢です。
1. 責務の分離:フロントとバックの「いいとこ取り」
この構成の最大の強みは、クライアント制御とサーバー制御の役割が明確に分かれている点にあります。
| レイヤー | サービス | 主な役割 |
|---|---|---|
| クライアント制御 | Firebase | ユーザー認証(Auth)、デバイス整合性の検証(App Check)、ホスティング |
| ロジック・認証制御 | Cloud Run | ビジネスロジックの実行、IAMによるGemini呼び出し、レート制限、ログ監視 |
Firebaseだけで完結させようとすると、「サーバーサイドのロジックが薄くなる」「ユーザー単位の厳密なクォータ制限が難しい」といった壁にぶつかります。そこにCloud Runを介在させることで、「誰が、どの権限で、どれだけリソースを使ったか」を完全に掌握できるようになります。
2. 実務レベルの信頼チェーン:トークン検証の徹底
単に「並べる」だけでは不十分です。この構成をセキュアに保つための「つなぎ込み」が重要です。
- Firebase IDトークンの検証: Cloud Run側で、リクエストに含まれるFirebaseのIDトークンを検証します。「ログイン済みの正当なユーザーか」をサーバー側で再確認することで、なりすましを防ぎます。
- App Checkトークンの検証: サーバー側でApp Checkトークンをチェックし、リクエストが「改造されていない正規のアプリ」から来たことを担保します。
3. 規模に応じた拡張パス(スケーラビリティ)
この構成は、サービスの成長に合わせて柔軟に進化させることができます。
パターンA(シンプル): まずはここから。IAMでキーを排除する
Client
↓
Cloud Run API
↓
Gemini
パターンB(上記): ユーザー識別とデバイス検証を追加し、不正利用を徹底排除する
Client
↓ Firebase Auth / App Check
↓
Cloud Run
↓
Gemini
パターンC(さらに本格): 非同期処理やキューイングを導入し、大規模なトラフィックやコスト爆発を物理的に制御する
Client
↓ Firebase
↓
Cloud Run
↓
Pub/Sub / Queue
↓
Worker
↓
Gemini
6. 移行戦略とバランス
とはいえ、すべてのプロジェクトで最初からバックエンドを立てるのは重いかもしれません。現実的な使い分けは以下の通りです。
ケーススタディ:適材適所の選択
- Firebase AI Logic / APIキーが許容されるケース
- 個人開発、クローズドなPoC、MVP(最小機能版)
- 最悪、プロジェクトが止まっても社会的な信用毀損が少ない用途
- バックエンド + IAMが必須のケース
- 一般公開サービス、法人向けプロダクト
- 利用量に応じて課金が発生する、またはコスト爆発が許容できない用途
実務での移行ステップ
- Step 1: Firebase AI Logicで迅速に機能を検証する。
- Step 2: 利用量が増えてきた、あるいは公開範囲を広げるタイミングで重要処理をバックエンドへ移管。
- Step 3: 全面的なIAM認証へ移行し、クライアント側のAPIキーを無効化(削除)する。
結論
LLM APIにおける高額請求問題の本質は、「設定不足」ではなく「設計前提」 にあります。
Firebase AI Logicは、開発の入り口を広げてくれる素晴らしいツールです。しかし、エンジニアとして本番運用を目指すなら、「App Checkは信頼の証明ではなく、怪しくないことの証明に過ぎない」 という限界を知り、最終的には「APIキーを使わない設計」へと寄せていくべきです。
「APIキーをどう守るか」という思考を捨て、「そもそも守る必要がない構成」 を目指すこと。それが、開発者とユーザー、そして財布を守るための唯一の最適解です。
おわりに
セキュリティは設定で決まるのではなく、設計で決まります。
あなたのプロジェクトの「鍵」は、本当に安全な場所にありますか?