私は水曜日の午後、GeminiOmniの最初の公開バージョンをリリースしました。それは、Nano Banana 2画像エディタの初期バージョンという単一の動作ツールと、「無料で試す、サインアップ不要」というボタンがある、簡素化されたランディングページでした。
サイトが公開されたのはベルリン時間の14:42。15:05には、私のGoogle Cloud請求ダッシュボードが警告を発していました。
この記事では、何が問題だったのか、その費用、そして同じ営業日内に私が何を変更したのかについて、完全な分析を提供します。有料のAI APIを扱うものを構築しているなら、これを無料の教訓として扱ってください。ほとんどの人は、恥ずかしい部分について書きませんから。
実際の流れ
以下は、私自身のインシデントログからのタイムラインです(はい、つけています。お勧めします)。
- 14:42 デプロイ成功。サイトがgeminiomni-ai.comで公開。小さなMastodonインスタンスに簡単なデモを投稿し、コーヒーを淹れに行く。
- 14:58 Google CloudからのSlack通知に気づく:「現在のサイクルの請求額が5ドルを超えました。」サイクルが開始されてから16分しか経っていなかったため、これは警戒すべきことでした。
- 15:05 Gemini APIコンソールにログイン。過去20分間に、3大陸の38の異なるIPから412件の画像生成リクエストを確認。
- 15:08 サイトをオフラインにする。APIキーを無効化。まだ使用しない新しいキーを生成。
- 15:42 アーキテクチャの書き換えを開始。
- 18:30 修正を施したv2をリリース。サイトを再びオンラインに戻す。
- 23:00 4時間にわたるこの災難の総請求額:84.30ドル。
参考までに、API使用の最初の月全体の予算は200ドルでした。本来なら避けるべきだったミスにより、その42%を17分で使い果たしてしまいました。
私がやっていたこと
画像エディタの最初のバージョンは、次のように構成されていました。Next.jsのページにReactコンポーネントがあり、ユーザーのプロンプトとアップロードされた画像を受け取り、@google/genaiクライアントとNEXT_PUBLIC_GEMINI_API_KEYに埋め込まれたAPIキーを使用して、ブラウザから直接GoogleのGemini Image APIを呼び出していました。
そうです。有料のAPIキーをNEXT_PUBLIC_*に設定しました。Next.jsはそれをクライアントJavaScriptに忠実にバンドルし、DevToolsを開く人なら誰でも見ることができる状態になりました。
弁解するなら(あまり良い弁解ではありませんが)、元のプロトタイプはプライベートなlocalhostでの実験でした。素早く反復処理を行っていたため、APIルートを設定する手間を省きたくて、キーをクライアントサイドの環境変数として配線していました。公開デプロイの準備をする際に、約40の変更を行いましたが、キーの場所はそのうちの一つではありませんでした。
このミスは私だけのものではありません。2024年から2025年にかけて、私が尊敬する6人ほどが、このバリエーションを経験しています。これは私が見た中で最も一般的な、防止可能なAIコストインシデントです。
なぜそんなに早くスクレイピングされたのか
私が驚いたのは、リクエストがどれほど早く来始めたかという詳細です。私はせいぜい600人程度のフォロワーがいるMastodonインスタンスに投稿しましたが、その中の誰かが悪意を持って行ったとは思えません。トラフィックは他の場所から来ているに違いありません。
リクエストログから判明したことは次のとおりです。
- 公開から5分以内に、自動化されたクローラーがサイトにアクセスし、JavaScriptからバンドルされた
NEXT_PUBLIC_GEMINI_API_KEYを抽出し、私が知らなかったキー取引用のチャネルに公開しました。 - 10分以内に、そのキーは約38の異なるIPによって使用され、私の製品とは無関係の画像を生成していました。ログでサンプリングしたプロンプトのほとんどは一般的なもの(「赤いスポーツカー」、「森の中のアニメの少女」)でした。いくつかは、敵対的なプロンプトでモデルの限界をテストしていました。
- 15分目までに、誰かがGoogleが許可する限り高速で画像を生成するタイトなループを実行していました。
これが、インディー開発者に理解してほしい部分です。APIキーをスクレイピングすることだけを仕事にしているクローラーが存在します。 彼らは最近の発行されたSSL証明書を監視し、新しいドメインにアクセスし、JavaScriptバンドルを解析し、発見したキーを数分以内に公開します。有名である必要はありません。人気のサブレディットにいる必要もありません。キーが漏洩した状態でオンラインになっているだけで十分です。
その日の午後にリリースしたアーキテクチャ
修正は表面的なものではなく、構造的なものでした。APIキーがブラウザに送信されることが決してないようにデータフローを書き換え、このアーキテクチャが現在も使用されています。
ブラウザ(キーは一切なし)
↓ POST /api/ai/generate { prompt, imageFile }
Next.js APIルート(サーバーサイド)
↓ envConfigs.gemini_api_key(サーバー上に保持)
Google Gemini API3つの具体的な変更点:
1. APIキーをサーバー専用の環境変数に移動。 NEXT_PUBLIC_GEMINI_API_KEY を GEMINI_API_KEY に変更。Next.jsはデフォルトでサーバー専用の環境変数をクライアントに送信しません。念には念を入れ、API_KEY または SECRET を含む環境変数名が NEXT_PUBLIC_ で始まる場合、アプリをクラッシュさせる起動時チェックを追加しました。
2. /api/ai/* プロキシを作成。 すべてのモデル呼び出しは、単一のサーバーサイドエンドポイントを経由します。ブラウザはプロンプトとユーザーがアップロードしたファイルを送信し、サーバーがAPIキーを添付して実際のGemini API呼び出しを行い、結果のみを返します。これは明白に思えますが、これが修正のすべてです。
3. プロキシにIPごとのレート制限を追加。 キーが安全になった後でも、1人のユーザーがFree枠の生成をスパム送信してクレジットを消費するのを防ぎたかったのです。プロキシは、Free枠ではIPごとに1分間あたり10リクエスト、Pro+では1分間あたり60リクエストを強制します。これでは元のインシデントは防げなかったでしょう(攻撃者はIPをローテーションしていました)が、次のクラスの悪用をはるかに困難にします。
コードの総変更量は、追加が約180行、削除が約90行でした。「サイトオフライン」から「新しいアーキテクチャでサイト再公開」までに2時間48分かかりました。素晴らしいとは言えませんが、壊滅的でもありません。
次回は何を変えるか
正直な答えは次のとおりです。有料サービスのクライアントサイドAPI呼び出しを含む公開AIアプリを決してリリースしてはいけません。 「単なるプロトタイプ」でも、「MVPテスト」でも、「友人への簡単なデモ」でもありません。脅威モデルは悪意のあるユーザーではありません。それは、まさにこのシナリオのためにすでに存在する自動化されたインフラストラクチャです。
このインシデントから得た、さらにいくつかの教訓を紹介します。
出荷前にバンドルをチェックする。 出荷前に、grep -r "NEXT_PUBLIC_" .next/static/ を実行し、中身を確認してください。認証情報のように見えるものがあれば、公開する前に修正してください。現在はこれをデプロイ前のgitフックとして設定しています。
1日のハードキャップを設定する。 Google Cloudでは、Gemini APIの1日あたりのドル額を上限に設定できます。最初の月は50ドル、その後は200ドルに設定しました。上限に達すると、APIはエラーを返し、メールが届きます。これは、悪い夜に一度に5,000ドルまで請求がいくのを許すよりはるかに優れています。
出荷する最も低コストのモデルを使用する。 Free枠の表面で、Nano Banana 2の4K編集あたり0.151ドルを消費していました。攻撃の前でさえ、ユニットエコノミクスは脆弱でした。現在は、Free枠の生成を1編集あたり0.045ドルのNano Banana 2Kにルーティングし、4KはPro+に残しています。3.4倍のコスト削減により、84ドルの請求額は約25ドルになっていたでしょう。
プロンプトの長さに上限を設ける。 悪用者の1人は、埋め込み画像データを含む巨大なプロンプトを送信していました。Geminiはトークンごとに課金します。プロキシは現在、受信するプロンプトサイズを50KBにハードキャップしています。正当なユーザーがこれに抵触したことは一度もありません。悪用者は最初のリクエストで抵触します。
証拠
私がこれをでっち上げているのではないことを確認したい方のために:私の4月28日のGemini APIコンソールには、14:42から15:08までの411件のリクエストが表示されており、支出レポートには画像生成で83.96ドル、入力トークンで0.34ドルの料金が表示されています。その夜、Google Cloudサポートにメールしましたが、返金は断られました。キーは正当に私のものであり、使用を許可していたので、当然のことです。彼らは、私がすでにリリースしていたのとまったく同じアーキテクチャを提案しました。
部屋の反対側からでも読める大きさで書かれた重要なポイントは次のとおりです。
AI APIキーはベアラ認証情報です。サーバー上に置くべきであり、他の場所に送ってはいけません。
この投稿から一つだけ学ぶなら、これを学んでください。
関連記事
— Lena
