1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

API キーを直書きしそうになったので、env と quota で守る形にした

1
Posted at

API キーをコードに直書きしそうになって、ちょっと冷や汗をかきました。

LLM API を触っていると、手元で検証したいだけの瞬間があります。エラーを減らしたくて、SDK 初期化のところに一瞬だけ key を貼って、あとで消せばいいか、となりがちです。私はまさにそれをやりかけました。

ただ、API key は「あとで消す」ではだいたい負けると思っています。コミットに混ざる、スクショに映る、ログに出る、CI のキャッシュに残る、どこで残るかが読みづらいからです。

今回は説教っぽい話ではなく、自分の未遂をきっかけに、env、secret manager、quota、usage review の4つに分けて最低限の形に直したメモです。

3行まとめ

  • API key はコードに置かず、local は env、team と production は secret manager に逃がす
  • env に逃がしても漏れる前提で、quota と daily limit を小さく置く
  • 週1回、dashboard の usage と repo 内の直書き痕跡を見る

先に置いた前提

この記事では、LLM API を呼ぶ小さな SaaS や社内ツールを想定しています。個人開発からチーム運用に移るくらいの温度感です。

「完璧な secret management を作る」よりも、「今日うっかり key を貼らない」「貼っても被害が広がりすぎない」ことを優先しています。私はこの手の運用を最初から綺麗に作れないタイプなので、まず事故りやすい場所から塞ぐ方針にしました。

OpenAI の production best practices でも、API key はコードや public repo に出さず、environment variables か secret management service でアプリに渡す、という趣旨の説明があります。usage page で key の tracking を見られる点も、運用としてはかなり大事だと思いました。

まず repo 内を疑う

最初にやったのは、手元の repo に「もう貼ってないか」を見ることでした。

厳密な secret scanner の代わりではないですが、まず雑に grep します。false positive は出ます。出ますが、何も見ないよりはだいぶましです。

git grep -nE '(apiKey: *"|Authorization: Bearer |OPENAI_API_KEY=.*[^=])' -- ':!*.md' ':!*.lock'

私は最初、README.md のサンプルまで引っかかって少し面倒でした。なので、docs や markdown は用途によって除外してもよいと思います。ただし、本当に事故っている場合はドキュメントに貼っていることもあるので、最初の1回は除外しない方が安心です。

見る場所はだいたいこのあたりです。

場所 見る理由
src/ SDK 初期化に直書きされやすい
scripts/ 検証用スクリプトに残りやすい
.github/workflows/ curlAuthorization に混ざりやすい
README.md 動作例として本物を貼りがち
.env.example example のつもりで実値が入ることがある

CI でも軽く止める

次に、pull request で雑に止めるチェックも足しました。これは専用ツールの代替ではありません。あくまで「直書きっぽいものが混ざったら会話を始める」ための軽い網です。

name: secret-check

on:
  pull_request:

jobs:
  secret-check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Check suspicious API key usage
        run: |
          if git grep -nE '(apiKey: *"|Authorization: Bearer )' -- ':!*.md' ':!*.lock'; then
            echo "possible hardcoded API key"
            exit 1
          fi

ちゃんとやるなら secret scanning や専用 scanner を入れるべきです。ただ、最初の一歩としては、SDK 初期化や curl の Authorization header を PR 上で見つけるだけでも効果がありました。

env に逃がすだけでは足りなかった

直書きしないために、まず local では env から読む形にしました。

やりかけた悪い例はこうです。

// bad
const client = new OpenAI({
  apiKey: "ここに実キーを貼る",
});

直した例はこうです。

import OpenAI from "openai";

const apiKey = process.env.OPENAI_API_KEY;

if (!apiKey) {
  throw new Error("OPENAI_API_KEY is not set");
}

export const client = new OpenAI({ apiKey });

これだけだと普通の話ですが、ポイントは「env にしたから安全」ではなく、「コードレビューで key の値を見なくてよくする」くらいに捉えることだと思っています。

env は普通に漏れます。shell history に残ることもありますし、debug log に出してしまうこともあります。production やチーム運用なら、GitHub Actions secrets、AWS Secrets Manager、GCP Secret Manager、Vercel / Cloudflare などの secret 管理に寄せる方が自然です。

.env.example に残すもの

.env.example には値を入れず、名前と意図だけ残しました。

OPENAI_API_KEY=
FLATKEY_API_KEY=
AI_GATEWAY_BASE_URL=https://router.flatkey.ai/v1
LLM_DAILY_TOKEN_LIMIT=50000
LLM_WEEKLY_BUDGET_USD=20

ここで LLM_DAILY_TOKEN_LIMITLLM_WEEKLY_BUDGET_USD も一緒に置いたのが、自分の中ではよかったです。key そのものだけを見ると secret management の話で終わりますが、実際に怖いのは漏れた後に使われ続けることだからです。

quota を小さくして爆発範囲を決める

API key を env に移しても、漏れた瞬間に無制限で使えるなら怖さは残ります。なので、私は quota を「本番の都合」ではなく「漏れた時の爆発範囲」として見ることにしました。

たとえば個人開発や小さな検証なら、最初から大きい上限はいらないです。

用途 最初の quota 方針
local 検証 1日で使い切っても痛くない上限
preview 環境 production よりかなり小さい上限
production alert と usage review 前提で必要量だけ
batch / agent job 単位の上限と停止条件を置く

私はここで少し反省しました。以前は「動かないと困るから余裕を持たせる」と考えがちでした。でも LLM API は、うっかりループや agent の暴走で使い方が跳ねます。余裕を持たせるなら、同時に review の仕組みも置かないと片手落ちでした。

個人開発の暫定ルール

今はざっくりこうしました。

  1. local key は検証用として quota を小さくする
  2. production key と local key を分ける
  3. preview key は production より小さい quota にする
  4. 使わなくなった key は消す
  5. 週1回、usage と key 一覧を見る

特に「local と production を分ける」は大事でした。local で雑に試す key と、本番ユーザーのリクエストを受ける key が同じだと、調査がしんどくなります。

usage review を週次にした

env と quota まで入れても、最後は見ないと気づけません。

なので、週次で usage review を見ることにしました。細かい FinOps をやるというより、変な増え方をしていないかを見ます。

見る項目

見る項目 気にすること
key ごとの usage local key が妙に増えていないか
model ごとの usage 高い model に意図せず流れていないか
project / environment preview や batch が増え続けていないか
error と retry 失敗 retry で token を溶かしていないか
quota 到達 上限が低すぎるのか、使い方が荒いのか

OpenAI 側では Usage page や API key tracking を見る、gateway を挟んでいるなら gateway 側の dashboard も見る、という形です。key を複数作っている場合、どの key がどの用途か名前で分かるようにしておくのも地味に効きました。

Flatkey AI ではどう見るか

Flatkey AI は、複数の AI model を1つの OpenAI-compatible base URL と1つの key で呼べる API gateway です。local の知識ベース上では、https://router.flatkey.ai/v1 を base URL として、API keys、usage tracking、billing、quota limits、routing configuration を dashboard で見る前提になっています。

この手の gateway を使うときも、私は「key が1つで便利」で止めない方がよいと思いました。むしろ見る場所を集約できるので、次のように運用ルールへ落とせます。

やること Flatkey 側で見たいもの
key を用途ごとに分ける API key 一覧
上限を小さく始める quota limits
model の使い分けを見る routing と model usage
請求前に気づく usage tracking と billing
週次で棚卸しする dashboard の usage review

宣伝っぽくしたいわけではなく、key management は「どこで止められるか」と「どこで気づけるか」が大事です。1つの dashboard で key、quota、usage、billing を見られるなら、週次レビューの対象に入れやすいです。

やらなかったこと

逆に、今回はやらなかったこともあります。ここを混ぜると、最初の事故防止としては重すぎると思ったからです。

やらなかったこと 今回やらなかった理由
全社向けの複雑な権限設計 まず個人開発と小チームの直書き防止が目的だった
自動 key rotation rotation の前に、用途分離と不要 key 削除を優先した
完全な secret scanner 導入 最初は PR で会話できる軽い検知を優先した
独自の課金アラート基盤 dashboard と quota の定期確認から始めた

こういうものは、もちろん必要になる場面があります。ただ、最初から全部を入れようとすると、面倒になって結局なにも入らないことがあります。私はそのパターンを何度かやっています。

なので今回は、repo に貼らない、用途ごとに key を分ける、quota を小さくする、週次で usage を見る、という小さい運用に寄せました。あとから強くする前提で、まず人間が続けられる形にする方がよさそうでした。

小さくても、続けられる運用の方が事故には強いと思います。

ハマったポイント

一番ハマったのは、直書き防止を secret management だけの話にしてしまうことでした。

実際には4層くらいあります。

目的
env / secret manager コードに key を置かない
repo check 直書きの混入に早めに気づく
quota 漏れた時の被害を小さくする
usage review 変な使われ方に気づく

env だけだと、漏れた後に止まりません。quota だけだと、そもそも key が repo に残ります。usage review だけだと、気づくまでの消費が読めません。

最初から全部を完璧にするのは重いですが、直書きしそうになった瞬間にこの4つを小さく入れるのが、自分にはちょうどよかったです。

まとめ

今回やったことは地味です。

  1. repo 内に直書きがないか見る
  2. local は env、team と production は secret manager に寄せる
  3. key を用途ごとに分ける
  4. quota を小さく始める
  5. 週1回 usage と key 一覧を見る

ただ、LLM API は key ひとつで実コストが動くので、この地味さがそのまま安心につながると思っています。

個人的には、API key 管理は「隠す」だけでは足りなくて、「漏れた時に小さく止まる」「増え方に気づく」まで含めて運用するのがよさそうでした。

参考

おわりに

API key を貼りそうになった時点で負け、というより、貼りそうになる前提で止め方を作るのが現実的だと思いました。

自分の repo で API key の直書きがないか見て、quota と usage review を週次で見る。まずはここからで十分かもしれません。

間違いあったらコメントください。よろしくお願いします。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?