記事一覧へ戻る

ゲームに配る API キーと、分析に使う API キーを分ける

配布するゲームには、テレメトリを送る権限が必要です。 ビルドを比較するジョブには、そのデータを読む権限が要ります。 同じキーを両方に渡すと、ゲームの配布物から分析データへのアクセス権限まで取り出されるおそれがあります。

Framedash では、ゲームには Ingest キーを、通常の分析には別の Read-only キーを使います。 どちらも接頭辞は fd_ です。 サーバーが認可の判断に使うのは、キーに保存された scope(許可された操作の集合)です。 キーの名前や接頭辞から権限は決まりません。

操作に合わせてプリセットを選ぶ

プロジェクトの API キー画面では、次のプリセットを選べます。 scope の組み合わせは、API 概要に記載されています。

プリセット保存される scope用途
Ingestevents:writeSDK からテレメトリを送る
Read-onlyanalytics:read集計、プロジェクトの状態、設定を読む。ビルドを比較する
Read & Writeanalytics:read, resources:write読み取りに加え、マップ、コンテンツ、アラートを変更する
Fullanalytics:read, resources:write, data:adminさらに raw SQL、データのエクスポート、プレイヤーのテレメトリ削除を実行する

Full に events:write は含まれません。 ゲーム用の Ingest キーの代わりには使えません。 各プリセットは、用途に合わせた権限の組み合わせです。 上位のプリセットで下位の操作もすべてできる、と考えると取り込み権限を誤解してしまいます。

raw SQL を使う場合は、付与する権限を確認してください。 SELECT でもプレイヤー単位の行を取得できるため、/api/v1/query には data:admin が必要です。 この scope には、エクスポートとプレイヤーデータの削除も含まれます。 クエリを通すために Full を選ぶ場合も、追加で渡す権限は同じです。 集計 API で調査できるならそちらを使い、より広い権限は必要な管理下のプロセスに限定します。

キーはプロジェクトに紐づきます。 別のプロジェクト ID を指定しても、そのプロジェクトへの権限は得られません。 scope があっても、アカウントの制限やレート制限は適用されます。

分析用のキーを配布物に入れない

二つの経路を分ける構成図。配布するゲームには events:write のキーだけを持たせて Ingest API に送信し、管理下の分析環境では analytics:read のキーで Web API から読み取る。キーは相互にコピーしない。

図の上段では、SDK のイベントを https://ingest.framedash.dev/v1/events に送ります。 下段の読み取り先は https://app.framedash.dev/api/v1 です。 分析用のキーは、管理下の開発マシン、バックエンド、信頼できる CI ランナーに置きます。 ゲームのアセット、Web ゲームの JavaScript、ダウンロード可能な設定、公開するビルド成果物には含めません。

クライアントに配るキーは、取り出される前提で扱います。 ソースコードの値をビルド時の環境変数に移しても、最終的な配布物へ埋め込まれれば秘密にはなりません。 難読化を理由に、より強い権限のキーを配らないでください。 アプリに埋め込んだ秘密情報を抽出される問題は、OWASP の解説でも扱われています。

性能チェックでは、ゲームのプロセスに Ingest キー、比較する CLI に Read-only キーを渡します。 必要なプロセスだけがキーを受け取る構成にしてください。 CLI の --api-key-file で非公開のファイルを指定すれば、キーの値をコマンドライン引数に含めずに済みます。 ただし、同じマシンやワークスペースのファイルを読めるコードから、そのキーを守れるわけではありません。

Ingest キーの露出で残るリスク

Ingest キーでは、分析データの読み取り、マップの変更、既存のプレイヤーテレメトリの削除はできません。 一方、そのプロジェクトへのイベント送信はできます。 キーを取り出した人は、取り込み先が受け付ける形式と制限の範囲内で、偽のテレメトリを送信できます。

偽のイベントが入ると、測定結果が汚染されたり、取り込みの処理枠やイベント枠を消費されたりするおそれがあります。 API キーで確認できるのは、送信に使われた認証情報の権限です。 フレームタイムが改変されていないゲームの実測値か、申告されたプレイヤー ID やビルド ID が本物かまでは証明しません。 取り込み専用にしても、不正なイベント送信は防げません。

リリース判断に使う測定は、できる範囲で公開クライアントのテレメトリから分けます。 隔離が必要なら、プロジェクト自体を分ける方法があります。 クライアントが申告するビルド ID を変えるだけでは、送信元の正当性を確認したことにはなりません。 不自然な増加は、自分たちのランナーの記録と突き合わせます。 より厳密な送信元の検証が必要な場合は、管理下のバックエンドで検証方法を設計してください。 キーの分離だけでは、その検証はできません。

CI とエージェントへ渡す権限

CI のシークレットは、それを受け取るコードから利用できます。 分析用や管理用のキーを読めるジョブで、未信頼のプルリクエストのコードを実行しないでください。 GitHub のセキュリティガイドでは、未信頼コードのチェックアウト、スクリプトインジェクション、ログのマスキングの限界を扱っています。 ワークフローの変更をレビューし、権限のある分析ジョブと未信頼のビルドを分けます。

分析エージェントには、まず analytics:read と調査に必要なツールだけを渡します。 テレメトリ中の文字列、リポジトリ内のファイル、ツールの結果は、未信頼の入力として扱ってください。 そこに書かれた指示を根拠に、秘密情報を読んだり、キーを別の宛先へ送ったり、scope を広げたりしない設計にします。 キーの値は、プロンプトやツールの出力に貼り付けません。

最小権限にすると、誤って実行したツール呼び出しで可能な操作を絞れます。 ただし、Read-only でもプロジェクトの情報を読めるため、エージェントが結果を送れる先も管理する必要があります。 「データを変更しないで」というプロンプトだけで、サーバー側の scope 制限を代用することはできません。

イベントを送らずにキーを確認する

GET https://app.framedash.dev/api/v1/whoami は、X-API-Key で送ったキーを検証します。 Ingest キーも使えるので、確認のためにテレメトリをアップロードする必要はありません。 このエンドポイントは API キー専用で、OAuth Bearer トークンは受け付けません。

次の Python スクリプトは、シークレット管理からプロセスに渡した環境変数を読みます。 キーの値をコマンドライン引数に載せたり、出力したりしない例です。 実行時に環境変数の一覧やデバッグログを出さないようにしてください。

import json
import os
import urllib.request

request = urllib.request.Request(
    "https://app.framedash.dev/api/v1/whoami",
)
request.add_unredirected_header("X-API-Key", os.environ["FRAMEDASH_API_KEY"])
with urllib.request.urlopen(request, timeout=15) as response:
    data = json.load(response)["data"]
print(json.dumps({"projectId": data["projectId"], "scopes": data["scopes"]}))

返ってきたプロジェクト ID と scope が、そのジョブの用途に合っているか確認します。 Ingest 専用キーの応答は projectId と scopes のみで、分析や管理の scope があるキーには追加のメタデータが返ります。 無効または失効済みのキーは 401 になり、ほかにもアカウントの制限やレート制限で拒否される場合があります。 検証に成功すれば、キーが現在どのプロジェクトに紐づき、何を許可されているかが分かります。 取り込んだデータの完全性や真正性までは確認できません。

次のリリース前に、配布するゲームと CI の認証情報の設定を点検してください。 分析用のキーを公開成果物に入れてしまった場合は、そのキーを失効させ、管理下の利用先で新しいキーに置き換え、露出したコピーも取り除きます。 配布済みの Ingest キーは、クライアントの更新と合わせて交換を計画します。 古いキーを失効させると、そのキーを使い続けるクライアントからのテレメトリも止まるためです。 どのビルドやジョブがどのキーを使うかを記録しておくと、障害時にこの影響を判断できます。