ブラウザIDEのAPIキーと環境変数を安全に扱う

どのブラウザIDEも、遅かれ早かれ同じ問いに直面します。キーはどこに置くのか。プロジェクトにはAPIエンドポイントが必要で、AIパネルにはプロバイダーのキーが必要で、デプロイにはアカウントのトークンが必要です。デスクトップ環境なら、これらの値はシェルのプロファイルや追跡されていないファイルに置けます。ブラウザのタブでは境界が異なります。ページと一緒に配信されるものはページから読めますし、ブラウザプロファイルに保存されたものはそのプロファイルに属します。このガイドでは実用的なモデルを整理します。公開してよい値、クライアントに決して届けてはいけない値、GB Coderがプロバイダーの資格情報をどう扱うか、そしてキーが漏えいしたときの対処です。ブラウザのタブを秘密の金庫だと思い込まず、ブラウザIDEの利便性を享受したい人に向けています。

1. ブラウザのタブは公開されたビルド対象

最初のルールは不愉快ですが単純です。静的Webアプリは秘密を保持できません。ブラウザはHTML、CSS、JavaScriptをダウンロードし、ユーザーのマシンで実行します。誰でもDevToolsを開き、ネットワークパネルを読み、バンドルを検索し、ブレークポイントを設定して変数を検査できます。コードを難読化したり、チャンクに分割したり、設定ファイルに値を隠したりしても、変わるのは手間だけで結果は変わりません。ブラウザIDEはプロジェクトコードを実行するサンドボックスを追加しますが、IDE自体のUIは同じオリジンで動き、必要な値を読まなければなりません。サンドボックスは、UIが使う値の金庫ではありません。有用な判定は単純です。タブで動くコードから参照できる値は、タブを使う人にも読めると考えてください。同じオリジンで動くスクリプトにも読めると考えてください。これはブラウザ開発を危険にする話ではありません。境界を定義する話です。公開識別子は置けますが、サーバーの資格情報は置けません。

2. 静的アプリにおける環境変数の意味

Node.jsサーバーでは、環境変数は実行時にプロセスから読み取ります。Vite、Create React App、Next.js、Astroのビルドでは、公開環境変数の扱いが異なります。バンドラーがビルド時に参照をリテラル文字列へ置き換えるのです。ViteはVITE_接頭辞の変数をimport.meta.env経由で公開し、Create React AppはREACT_APP_、Next.jsはNEXT_PUBLIC_、AstroはPUBLIC_を使います。接頭辞は命名規則であり、セキュリティ制御ではありません。値がインライン化された時点で、それはすべての訪問者がダウンロードするJavaScriptの一部になります。公開接頭辞のない変数は通常クライアントコードから利用できないため、サーバー専用の値は静的ページではなく、サーバーまたはサーバーレス関数で読む必要があります。.envファイルは開発とビルドの入力であり、実行時の秘密保管庫ではありません。ローカル設定やバージョン管理外での値の維持には役立ちますが、ビルド後に値を非公開にはしません。

# Safe to expose: a public endpoint
VITE_API_URL=https://api.example.com

# Never expose: keep this on a server
STRIPE_SECRET_KEY=REPLACE_WITH_SERVER_SECRET

3. ブラウザに置いてよいキー

公開されることを前提としたキーもあります。Stripeの公開可能キーはアカウントを識別し、支払いトークンを作成します。秘密キーは実際に資金を動かすため、サーバーに置かなければなりません。Supabaseのanonキーは、行レベルセキュリティのポリシーが正しい場合にのみ安全です。FirebaseのWeb設定はプロジェクトを識別しますが、管理者アクセスを付与するものではありません。地図や分析のキーは、多くの場合ドメインやAPIで制限されます。これらの値はブラウザに配信できますが、許可オリジン、API制限、最小権限のロール、クォータ、アラートといった制限は必要です。反対のカテゴリはもっと簡単です。サービスロールキー、データベースのパスワード、SMTP資格情報、署名秘密、AIプロバイダーの秘密キーは、静的バンドルに入れてはいけません。アプリケーション自身のプロバイダーキーは特に危険です。見つけた人がアプリケーションの資金を使えます。ユーザー自身のキーは別の話です。Bearer資格情報であることに変わりはありませんが、持ち主はユーザーであり、目的はプロジェクトやリポジトリではなく、そのユーザーのブラウザに置くことです。

4. GB Coderがプロバイダー資格情報を扱う方法

GB CoderのAIパネルはbring-your-own-key方式です。キーマネージャーは既定でプロバイダーキーをメモリに保持します。localStorageには書き込みません。ユーザーが明示的に選んだ場合だけsessionStorageに保存し、その場合は現在のタブに限定され、通常はタブを閉じると消えます。ユーザーがキーを消去すると、メモリとセッションストレージから削除されます。マネージャーは各キーを許可されたエンドポイントのオリジンに結び付け、送信ゲートが、承認されていない宛先に資格情報が送られないか確認します。プロバイダーのエラーは秘匿化レイヤーを通り、メッセージがUIやログに届く前にキーのパターンと有効なキーを除去します。カスタムプロバイダーのメタデータはlocalStorageに保存されますが、秘密としてマークされたヘッダーは保存前に消去され、再入力が必要です。リクエストはブラウザからプロバイダーのエンドポイントへ直接送られ、GB CoderがLadeStackのサーバー経由でキーを中継することはありません。これらの対策は、偶発的な保存とオリジン間の漏えいを減らします。ただし基本ルールは変わりません。同じオリジンで動けるスクリプトは、ページが読めるものを読めます。機密作業にはクリーンなプロファイルを使い、信頼できない拡張機能を入れないでください。

5. プロジェクトの秘密、.envファイル、ビルド時のインライン化

秘密はプロジェクトツリーの外に置きます。.envとそのローカル変種を.gitignoreに追加し、プレースホルダー名だけの.env.exampleをコミットします。Viteを使うプロジェクトでは、VITE_変数がすべて本番バンドルにインライン化されることを忘れないでください。ローカル開発で便利だった値も、本番ビルドで使われた瞬間に公開されます。デプロイ前に生成されたアセットをキーのパターンで検査し、見つかったら警告ではなくインシデントとして扱います。GitHubのシークレットスキャンとプッシュ保護は既知のトークン形式をいくつか検出できますが、あくまで最後の網であり、値をリポジトリの外に保つ代わりにはなりません。ブラウザIDEのZIPエクスポートにはプロジェクトファイルが含まれます。.envがプロジェクト内にあれば、バックアップと一緒に移動します。バックアップは暗号化された金庫ではありません。エクスポートは非公開の場所に保存し、不要になった古いコピーは削除してください。スクリーンショット、Issue、チャットメッセージも同じです。そこに貼られたキーは、すでにブラウザの外に出ています。

# .gitignore
.env
.env.local
.env.*.local

6. GitHubトークンとデプロイトークン

GitHubの個人アクセストークンとデプロイトークンは、見た目より強力です。repositoryとworkflowのスコープを持つクラシックトークンは、非公開コードを読み、ファイルを変更し、リリースを起動できます。単一リポジトリ、明示的な権限リスト、有効期限を持つfine-grainedトークンを優先してください。トークンにはタスクに必要な権限だけを与えます。同期ならcontentsの読み書きだけで、administrationやbillingは不要です。デバイスや用途ごとに別のトークンを作り、作業が終わったら失効させます。VercelやNetlifyのデプロイトークンは、プロジェクトの公開、置換、削除ができます。プロバイダーがプロジェクト単位やチーム単位のスコープに対応しているなら使い、有効期限に対応しているなら設定してください。トークンは必要なときだけデプロイパネルに貼り、プロジェクトファイルには保存しないでください。CIではコミットされた.envではなく、リポジトリや組織のシークレットを使います。フォークからのpull requestには通常シークレットが渡らないことも覚えておきましょう。スクリーンショットに写した、チャットで共有した、公開リポジトリにコミットしたトークンは、1秒後に削除したとしても侵害されたものとして扱います。

7. AIプロバイダーキー:BYOKを予想外の事態なく使う

AIプロバイダーのキーは、請求書が付いたBearer資格情報です。持っている人は誰でもリクエストを送り、クォータを消費し、料金を発生させられます。プロジェクト単位に制限されたキーを使い、プロバイダーが提供するなら支出上限を設定し、組織のマスターキーではなく実験用の別キーを使ってください。特にデバイスを共有した後や新しい拡張機能を入れた後は、使用量を定期的に確認します。Ollama、LM Studio、vLLMなどのローカルプロバイダーはクラウドキーを必要としませんが、エンドポイントはマシン上のサービスです。デモを動かすためだけにローカルエンドポイントをインターネットへ公開しないでください。カスタムプロバイダーがプロキシを指す場合、プロキシはキーを見られます。自分が管理し、理解しているプロキシだけを使ってください。プロンプトインジェクションも関連するリスクです。ファイル、Webページ、貼り付けたエラーメッセージには、アシスタントに向けた指示が含まれている可能性があります。秘密はプロンプトの外に置き、アシスタントに読ませるファイルにも入れないでください。会話にキーが現れたら、ローテーションしてください。

8. 共有コンピューター、拡張機能、XSS

共有されたコンピューターやラボのマシンには、別の手順が必要です。プライベートなブラウザプロファイルを使い、セッションへの保存を選ばず、終わったらタブを閉じます。ブラウザがキーの保存を提案したら断ります。残っている疑いがあれば、サイトのストレージを消去します。ブラウザ拡張機能も境界の一部です。広いホスト権限を持つ拡張機能は、ページの内容やストレージを読めることが多く、インストール後に悪意ある更新が届く可能性もあります。資格情報を扱う作業には最小限のプロファイルを使ってください。この文脈での典型的なWebリスクはクロスサイトスクリプティングです。攻撃者がIDEのオリジンでスクリプトを実行できれば、そのスクリプトはlocalStorageとsessionStorageを読み、ページが開いている間にリクエストを送れます。依存関係を最新に保ち、信頼できない第三者スクリプトを避け、デプロイしたサイトでContent Security Policyの導入を検討してください。クリップボードマネージャーや画面共有ツールも、コピーして表示するものを見ています。キーを移動した後にクリップボードを消去し、プロバイダー設定パネルを開く前に共有を一時停止してください。

9. 漏えいしたキーのローテーション

漏えいしたキーへの信頼できる対応はローテーションだけです。先に失効させることが重要です。新しいキーを準備している間も古いキーが有効なら、露出は続きます。新しいキーは必要最小限のスコープで作り、アプリケーションやブラウザの設定を更新し、値がビルドに含まれていたなら再デプロイし、古いキーが拒否されることを確認します。次に、プロバイダーの使用量と監査ログで、予期しない活動がないか確認します。Gitにコミットしたキーは、ファイルを削除するだけでは不十分です。値は履歴に残ります。先にローテーションし、git filter-repoやBFGなどのツールで履歴を書き換え、チームの合意を得てforce-pushし、共同作業者に再クローンを依頼します。ホスティングプラットフォームは古いビルドをキャッシュすることがあるため、古い値が古いバンドルから配信されないようにしてください。AIチャット、サポートチケット、スクリーンショットにキーを貼った場合は、保持されたものとしてローテーションします。最後に、何が起きたかを記録し、それを検出できたはずのチェックを追加し、次のキーの寿命を短くします。

10. 実践的なチェックリスト

プロジェクトがブラウザを離れる前に、この順序を使ってください。第一に、各値を分類します。公開識別子、ユーザー資格情報、サーバー秘密のどれかです。第二に、サーバー秘密を静的バンドルの外に置き、サーバーレス関数やAPIの背後に移します。第三に、公開接頭辞が必要な値だけを公開し、デプロイ前にビルド済みアセットを検査します。第四に、.envファイルをGitの外に置き、.env.exampleをコミットし、シークレットスキャンを有効にします。第五に、GitHubとホスティング用に、用途ごとのスコープ付きで有効期限のあるトークンを作ります。第六に、GB Coderでは信頼できるデバイスでない限りキーをメモリに置き、トレードオフを理解したときだけセッションへの保存を選びます。第七に、キーをプロンプト、Issue、スクリーンショット、プロジェクトファイルに決して貼らないでください。第八に、少しでも疑わしければローテーションし、交換前に失効させます。第九に、共有マシンではクリーンなブラウザプロファイルを使います。第十に、必要になる前に漏えい時の計画を書いておきます。ブラウザを私的な金庫ではなく公開クライアントとして扱えば、ブラウザIDEでも資格情報を責任を持って管理できます。