コンテンツにスキップ

04. APIキーや秘密情報を漏らさないための基本

セキュリティ編でいちばん実害が大きいのがこのテーマです。

APIキーの流出は、気づいたときには手遅れという性質を持っています。他人のクラウド利用料が自分に請求される、データベースを丸ごと消される、といった事故が実際に起きています。

そしてバイブコーディングでは、この事故が起きやすい条件が揃っています。AIは「とりあえず動かす」ためにキーを直接書きがちで、しかもそのコードを人間が読まずにGitへpushしてしまうからです。


  1. モバイルアプリに「隠せる場所」は無いという前提
  2. EXPO_PUBLIC_ の本当の意味
  3. 公開してよいキーと、絶対にダメなキーの見分け方
  4. 秘密情報の正しい置き場所
  5. .gitignore とGit履歴の落とし穴
  6. 漏らしてしまったときの対処
  7. 秘密情報チェックリスト
  8. まとめと次のステップ

1. モバイルアプリに「隠せる場所」は無いという前提

Section titled “1. モバイルアプリに「隠せる場所」は無いという前提”

最初に、身も蓋もない事実から始めます。

アプリのバイナリに含めたものは、すべて読み取られます。

.env に書いても、変数名を難読化しても、Base64でエンコードしても同じです。配布された .apk や .ipa は誰でも展開でき、中の文字列は簡単に取り出せます。ソースコードを公開していなくても関係ありません。

したがって、考え方はこうなります。

アプリに入れてよいのは「公開されても困らないもの」だけ。 困るものは、そもそもアプリに入れず、サーバー側に置く。

「PhaseEx:応用事項編」でGemini APIをCloud Functions経由で呼んだのは、まさにこの理由からです。


Expoでは、EXPO_PUBLIC_ で始まる環境変数がアプリのコードから読めます。

const key = process.env.EXPO_PUBLIC_FIREBASE_API_KEY;

ここで多くの人が誤解します。EXPO_PUBLIC_ は「安全に隠してくれる仕組み」ではありません。 名前のとおり、PUBLIC(公開される)という宣言です。

ビルド時にこの値はコードへ直接埋め込まれます。つまり、

  • EXPO_PUBLIC_ を付けた値 = アプリを配布した全員に見える値
  • .env ファイルに書いたかどうかは無関係
  • リポジトリが非公開かどうかも無関係

.env をGitに入れないのは、あくまでチーム内やGitHub上での取り扱いを整理するためであって、値そのものを秘密にする効果はありません。


3. 公開してよいキーと、絶対にダメなキーの見分け方

Section titled “3. 公開してよいキーと、絶対にダメなキーの見分け方”

すべてのキーが秘密というわけではありません。設計上、公開される前提のキーも存在します。

種類例アプリに入れてよいか
クライアント識別用の公開キーFirebase の apiKey、AdMob のアプリID、PostHog のプロジェクトAPIキー、RevenueCat の公開SDKキー入れてよい。ただし後述の防御が前提
サーバー用のシークレットGemini / OpenAI のAPIキー、Stripe のシークレットキー絶対にダメ。サーバー側に置く
管理者権限の資格情報Firebase Admin SDK のサービスアカウントJSON絶対にダメ。漏れたら全データを操作される
ストア・CI用の資格情報Apple の App Store Connect APIキー、Android の keystore、EXPO_TOKEN絶対にダメ。CIのSecretsに置く

「入れてよい」キーが安全な理由

Section titled “「入れてよい」キーが安全な理由”

Firebase の apiKey は、実は公開前提です。これはプロジェクトを識別するためのもので、アクセス権限を与えるものではないからです。誰がキーを知っていても、データを守るのは次の記事で扱うセキュリティルールの仕事です。

同じく、PostHog のプロジェクトAPIキーやAdMobのアプリIDも、クライアントに埋め込む前提で設計されています。


用途ごとに置き場所が決まっています。

① アプリから使う公開キー → .env + EXPO_PUBLIC_

Section titled “① アプリから使う公開キー → .env + EXPO_PUBLIC_”
Terminal window
# .env(Gitには入れない。値自体は公開されることを理解したうえで使う)
EXPO_PUBLIC_FIREBASE_API_KEY=AIza...
EXPO_PUBLIC_POSTHOG_API_KEY=phc_...

チームで共有する場合は、値を空にした .env.example をリポジトリに置いておくと親切です。

Terminal window
# .env.example(これはGitに入れてよい)
EXPO_PUBLIC_FIREBASE_API_KEY=
EXPO_PUBLIC_POSTHOG_API_KEY=

② サーバーから使うシークレット → Cloud Functions のSecret Manager連携

Section titled “② サーバーから使うシークレット → Cloud Functions のSecret Manager連携”

Gemini APIキーのように、絶対に見せられないものはサーバー側にだけ置きます。アプリはサーバーを呼ぶだけで、キーには触れません。

Terminal window
firebase functions:secrets:set GEMINI_API_KEY

登録だけでは関数から使えません。defineSecret('GEMINI_API_KEY') を定義し、関数の secrets オプションに関連付けてから実行時に .value() で読みます。応用事項編の実装例を参照してください。

③ ビルド・CIで使う資格情報 → EAS環境変数 / GitHub Secrets

Section titled “③ ビルド・CIで使う資格情報 → EAS環境変数 / GitHub Secrets”
Terminal window
# 値は対話入力し、シェルのコマンド履歴に書かない
eas env:create --environment production --name SENTRY_AUTH_TOKEN --visibility secret

GitHub Actions から使うものは、リポジトリの Settings → Secrets and variables → Actions に登録します。EXPO_TOKEN はここです。

EASのsecretはビルドサービス上での取り扱いを制限する設定です。その値をアプリへ埋め込めば、配布物から読み取れます。EAS環境変数の公式説明を確認してください。


Terminal window
# .gitignore に含まれているべきもの
.env*
!.env.example
*.keystore
*.jks
google-service-account.json

google-services.json と GoogleService-Info.plist は通常、公開前提のクライアント設定です。サービスアカウントの秘密鍵JSONとは別物です。運用方針でGitから除外しても構いませんが、その場合はEASのファイル型環境変数などでビルド時に供給してください。

履歴に残っていないか確認する

Section titled “履歴に残っていないか確認する”

.gitignore に足しただけでは、過去のコミットに入ったファイルは消えません。 一度でもpushしていたら、それは公開されたものとして扱ってください。

Terminal window
# 追跡対象に .env が入っていないか
git ls-files | grep -E "\.env|keystore|service-account"
# 過去の履歴に含まれていないか
git log --all --oneline -- .env
【タスク: 秘密情報の混入チェック】
このリポジトリに、コミットしてはいけない情報が含まれていないか点検してください。
### 確認してほしいもの
1. ソースコード中に直接書かれたAPIキー・トークン・パスワード。
2. `.gitignore` に入れるべきなのに追跡対象になっているファイル
(.env、keystore、サービスアカウントの秘密鍵JSON など)。
3. `EXPO_PUBLIC_` を付けているが、本来サーバー側に置くべき値。
### 出力形式
- 見つかった項目ごとに「ファイル」「該当箇所」「危険度」「対処法」を書いてください。
- 3番については、なぜサーバー側に置くべきかの理由も添えてください。

6. 漏らしてしまったときの対処

Section titled “6. 漏らしてしまったときの対処”

やってしまった場合、順番が重要です。

ステップ1:まずキーを無効化する(最優先)

Section titled “ステップ1:まずキーを無効化する(最優先)”

コードを直すより先に、発行元のコンソールでキーを失効させてください。履歴から消す作業をしている間も、漏れたキーは使われ続けます。

  • Google Cloud / Gemini:該当のAPIキーを削除して再発行
  • Firebase Admin:サービスアカウントの鍵を失効
  • Expo:EXPO_TOKEN を revoke して再発行
  • Apple:App Store Connect APIキーを失効

ステップ2:新しいキーを安全な場所に置く

Section titled “ステップ2:新しいキーを安全な場所に置く”

再発行したキーは、今度は .env や Secrets に置きます。同じ場所に戻さないでください。

ステップ3:請求と利用状況を確認する

Section titled “ステップ3:請求と利用状況を確認する”

各サービスの利用状況ダッシュボードを開き、身に覚えのないアクセスが無いかを確認します。心当たりのない急増があれば、すでに使われています。

ステップ4:履歴から消す(最後でよい)

Section titled “ステップ4:履歴から消す(最後でよい)”

git filter-repo などで履歴を書き換えることはできますが、すでに漏れたキーは戻せません。ステップ1を済ませた後の後始末として行ってください。GitHubの公開リポジトリだった場合、フォークやキャッシュに残っている可能性も考慮します。


  • ソースコードに直接書かれたキー・トークン・パスワードが無い
  • EXPO_PUBLIC_ が付いている値は、すべて「公開されてよい」と判断済み
  • サーバー用のシークレットは Cloud Functions 側にのみ存在する
  • .gitignore に .env / keystore / サービスアカウントJSON が入っている
  • git ls-files で秘密ファイルが追跡されていないことを確認した
  • 過去の履歴にも秘密情報が入っていないことを確認した
  • CI用のトークンは GitHub Secrets / EASの適切な環境にあり、配布アプリには埋め込まれていない
  • 公開キーの裏側に、権限の制御(セキュリティルール等)が効いている

覚えることは2つだけです。アプリに入れたものは全部見える。だから見えて困るものはサーバーに置く。

そして EXPO_PUBLIC_ は隠す仕組みではなく、公開の宣言です。ここを誤解したまま進むと、いつか事故が起きます。

公開キーが安全であるためには、その裏側で権限が正しく絞られていなければなりません。次の記事では、その最後の砦である「05. 認証・権限・データベース設定のセキュリティチェックリスト」に進みましょう。