Skip to main content

第13章 環境変数と秘密情報:ローカルでも事故らない🧪🔐

この章は「ローカルで動いてるのに、うっかり本番の秘密を触った」「APIキーをGitに入れてしまった」みたいな事故を、先に潰す回です😇🧯 結論から言うと――

  • ただの設定値 👉 .env / .env.dev / .env.prod などでOK ✅
  • 秘密(課金や権限につながるもの) 👉 Secret Manager + secretsバインド が基本 ✅
  • エミュレータだけで差し替えたい秘密 👉 .secret.local が最強 ✅ (Firebase)

(ここで言う「Functions」は Firebase の Cloud Functions for Firebase のことだよ🧩)


今日のゴール🎯✨

  • .env.env.local の役割が言える🙂
  • 秘密を Secret Manager に入れて、関数にだけ渡す(バインド)仕組みが分かる🔐
  • ローカルでだけ秘密を差し替える .secret.local が使える🧪
  • AI(Genkit / Gemini系)を使う時に「秘密を漏らさない設計」ができる🤖🛡️

1) まず「秘密」と「秘密じゃない」を分けよう🧠🔎

_secret_vs_config

✅ 秘密じゃないものの例

  • ENV=dev / FEATURE_X=true みたいな 動作モード
  • 画面表示の切り替え用フラグ
  • Firebase Webアプリの apiKey などの Firebase設定(これは“秘匿情報”扱いではない、ただし利用制限は大事)(Firebase)

「Firebaseの apiKey は見えてもOK」だけど、 “外部AIのAPIキー”決済キー は別物だよ⚠️🔥(=課金・権限に直結)

❌ 秘密の例

  • 外部AIサービスのAPIキー(例:Gemini API key)
  • 決済/メール送信/CRMなどのトークン
  • DB接続情報、サービスアカウント鍵、強い権限のトークン

2) .env の基本ルール🙂🧾

_env_hierarchy

Cloud Functions for Firebase は dotenv形式.env を読み込めて、コード側は process.env で参照できるよ🧩(Firebase) さらに プロジェクト(またはエイリアス)別.env.dev / .env.prod みたいに分けられるのが便利✨(Firebase)

例:functions/.env(みんな共通の“ただの設定”だけ入れる)

ENVIRONMENT=dev
AI_MODE=stub

コードから読む(Node/TypeScript例)

import { onRequest } from "firebase-functions/v2/https";

export const envCheck = onRequest((req, res) => {
res.json({
env: process.env.ENVIRONMENT ?? "unknown",
aiMode: process.env.AI_MODE ?? "unknown",
});
});

3) ローカルだけ上書きする .env.local が神🧪🔀

_local_override

エミュレータ起動時、.env.local.env.env.<project or alias> より優先されるよ💪(Firebase)

例:functions/.env.local(ローカルだけAIを必ずスタブに)

AI_MODE=stub
ENVIRONMENT=local

これで、うっかり本物のAI呼び出し(=課金)をしにくくなる😄💸🧯


4) .env に「秘密」を入れちゃダメ🙅‍♂️🔐

公式にもハッキリ書いてあって、.env安全な秘密保管庫ではないよ⚠️ Git管理に入った瞬間、事故の温床🥲(Firebase)

なので秘密はこうする👇

✅ 秘密は Secret Manager + secretsバインド

Cloud Functions for Firebase は Google Cloud Secret Manager と統合されていて、秘密を安全に扱えるよ🔐(Firebase)


5) 秘密を安全に使う最短手順🚀🔑

_secret_binding

ステップA:秘密を登録する

例:Gemini APIキーを登録(対話で値を入れる)

firebase functions:secrets:set GOOGLE_GENAI_API_KEY

「APIキーは課金される可能性があるから漏らさないでね」って注意まで書いてあるやつ😇(Firebase)

ステップB:その秘密が必要な関数だけに渡す

ポイント:secrets: [...] を書いた関数だけが読める🧠 (書き忘れると process.env.SECRET_NAMEundefined になる)(Firebase)

import { onRequest } from "firebase-functions/v2/https";

export const callAI = onRequest(
{ secrets: ["GOOGLE_GENAI_API_KEY"] },
async (req, res) => {
// ✅ secretsバインドした関数だけ参照できる
const key = process.env.GOOGLE_GENAI_API_KEY;

// ⚠️ 絶対にログやレスポンスへ出さない
res.json({ ok: true, hasKey: !!key });
}
);

6) エミュレータで秘密を差し替える .secret.local 🧪🧰

_local_secret

エミュレータは(権限がある場合)本番のSecret Managerを見に行こうとすることがあるんだけど、 CI環境などで失敗するケースがあるよね🤝💦

そこで .secret.localローカル専用の秘密に差し替えられる!(Firebase)

例:functions/.secret.local

GOOGLE_GENAI_API_KEY="dummy-local-key"

これでローカルではダミーキー、本番ではSecret Manager、って分けられる🧯✨(Firebase)


7) AIを絡めた実戦ミニ題材🤖🪄

_ai_mode_switch

ここから「自動整形ボタン(Functions)」に直結させるよ🔥 AIの呼び出しは2モードにするのが安心:

  • AI_MODE=stub 👉 ローカルは固定JSONを返す(課金ゼロ・安定)🙂
  • AI_MODE=real 👉 本番だけ本物のAIを呼ぶ(秘密はSecret Manager)🔐
import { onRequest } from "firebase-functions/v2/https";

export const formatMemo = onRequest(
{ secrets: ["GOOGLE_GENAI_API_KEY"] },
async (req, res) => {
const mode = process.env.AI_MODE ?? "stub";

if (mode === "stub") {
return res.json({
mode,
formatted: "【スタブ】ここに整形結果っぽい文章が入るよ🙂",
reason: "ローカルなので安全のためスタブ応答",
});
}

// realモード:本物のAIを呼ぶ想定(この章では骨組みだけ)
const key = process.env.GOOGLE_GENAI_API_KEY;
if (!key) return res.status(500).json({ error: "missing AI key" });

// ⚠️ keyをログに出さない!
// TODO: Genkit等でAI呼び出し
return res.json({ mode, formatted: "【real】AI整形結果(仮)" });
}
);

8) Genkit まで一気に繋ぐならこう🧩🤖

Genkit の onCallGenkit を使うチュートリアルでも、秘密は Secret Manager に入れて、関数に secrets で渡す流れになってるよ✅(Firebase) しかも例が「Gemini APIキーを漏らすと課金される可能性があるから危ない」ってめちゃ現実的😄💸(Firebase)


9) ついでに大事な地雷🧨😵‍💫

_legacy_trap

✅ 予約済みの環境変数名を使わない

.envFIREBASE_ / X_GOOGLE_ などの予約プレフィックスや、予約キーがあるよ。踏むと変な挙動になるので避けよう🧯(Firebase)

functions.config() は卒業方向

functions.config() は非推奨で、2027年3月以降は新規デプロイが失敗する予定だよ⚠️ 今から作る教材は Secret Manager 方式に寄せた方が安全🙌(Firebase)


10) AIで“漏洩チェック”をする🕵️‍♂️🤖

Gemini CLI の Firebase拡張は、Firebaseのドキュメント参照やコード支援、さらに Firebase AI Logic を使ったGemini APIの安全な利用コード生成みたいなプロンプト例まで用意されてるよ🧩(Firebase)

おすすめの使い方👇(この章の目的にドンピシャ)

  • functions/.env.env.local.secret.local の運用ルールを文章化して」📝
  • .gitignore に入れるべきものを提案して」🧯
  • 「ログに秘密が出ないようにコードをレビューして」👀🔐

ミニ課題🎯🧪

  1. AI_MODE=stub のとき、必ず固定JSONを返すようにする🙂
  2. AI_MODE=real のときだけ secrets を使うルートに入る🔐
  3. レスポンスにもログにも 秘密の値を絶対に出さないhasKey: true くらいまで)🫥✅

チェック✅✨

  • .env.local.env より強いのを説明できる🧠(Firebase)
  • 秘密は Secret Manager + secrets: で「必要な関数だけ」に渡せる🛡️(Firebase)
  • ローカルは .secret.local で秘密を差し替えられる🧪(Firebase)
  • Firebaseの apiKey と「外部AIのAPIキー」を混同しない🙂🧠(Firebase)

おまけ:ランタイム小ネタ🧾🧩

  • Cloud Functions for Firebase と Firebase CLI は Node.js 20/22 をフルサポート(Node 18は非推奨の流れ)(Firebase)
  • 「.NETで関数」系は Cloud Run functions 側の文脈になって、.NET 8 が 2nd gen でGAになった記録があるよ🧠(Google Cloud Documentation)

次の章(連携テスト)に向けて、もしよければ👇も一緒に作れるよ🙂🔥 「.gitignore.env.example のテンプレ」+「AI_MODEの切り替え手順書」を教材用に整えて、章末付録にしちゃおう📎✨