仕組みから学ぶ Web
応用読了目安 14#秘密情報#API キー#ローテーション#Secrets Manager

秘密情報の扱い — 消したコミットの中に、鍵はまだ残っている

誤ってコミットしたアクセスキーを次のコミットで消した。半年後にリポジトリを公開した数時間後、そのキーで高額なサーバーが大量に立ち上がった。漏れた鍵に効くのは削除ではなく、無効化。

この記事の進み方

What — 秘密情報は、どこから漏れるか

秘密情報(API キー、パスワード、署名用の鍵)は、書いた場所だけでなく、その値が通った場所すべてに残ります。

Figure鍵が漏れる経路
  1. 1Git の履歴消したコミットの前に残る重い
  2. 2コンテナイメージの層後の層で消しても、前の層に残る重い
  3. 3ビルドとデプロイのログecho や設定の表示
  4. 4設定と構成の定義環境変数、テンプレート、状態ファイル
  5. 5ブラウザに配るコードバンドルに入れば公開と同じ重い
  6. 6エラーと会話例外メッセージ、チャット、AI への貼り付け

冒頭の事故は 1。消すコミットを積んでも、追加したコミットは履歴に残る。

1/6
Git の履歴ファイルを消すコミットを積んでも、追加したコミットは履歴に残り、clone すれば誰でも取り出せる。フォーク、手元の clone、CI のキャッシュにも写しがある。

どれも「書いたつもりのない場所」に値が残る形。ソースコードに直接書かないだけでは、半分しか塞がない。

ローテーション
鍵を新しいものに差し替え、古い鍵を無効にすること。漏れていなくても定期的に行う。
無効化
発行元で鍵を使えない状態にすること。漏れた鍵に対して唯一効く対処。
シークレット管理サービス
秘密情報を暗号化して保管し、権限を持つ処理にだけ実行時に渡すサービス。
最小権限
その処理に必要な操作だけを許した鍵や権限にすること。漏れたときの被害を小さくする。
確認 — ここまで読めたか

API キーを書いたファイルを誤ってコミットし、プッシュしました。次のコミットでファイルを削除してプッシュしました。これで安全になりましたか?

まず選ぶ(解答例は a〜d の記号で説明します)

Why — なぜ「消す」では済まないのか

ファイルやログから値を消すのは、これから先に見つかる確率を下げる操作です。すでに誰かが読んだかどうかは変えられません。そして、読まれたかどうかは多くの場合確かめようがありません

だから、漏れた可能性がある鍵への対処は**「読まれた前提で、使えなくする」**しかありません。

消す:     これから見つかる確率を下げる       → 読まれた鍵は使われ続ける
無効化:   読まれていても使えなくする         → 被害が止まる

値を置く場所

「どこにも残らない場所」は存在しません。値が見える人と経路を、できるだけ少なくするのが目標です。

Compare秘密情報の置き場所

履歴に永久に残る

const apiKey = "sk_live_…"
見える人
**リポジトリを読めるすべての人**
残る場所
**Git の履歴、すべての clone**
差し替え
コードの変更とデプロイ
環境ごとの切り替え
できない
読み取りの記録
  • 冒頭の事故の入口
  • 消すコミットを積んでも、追加したコミットは残る
  • 本番と検証で違う鍵を使う、ということもできない

下に行くほど、値が見える人と経路が減る。代わりに、取り出す仕組みと権限の設計が要る。

このサイト自身の判断

このサイトは決済に Stripe を使い、そのキーを シークレット管理サービスに置いて、実行時に読んでいます。インフラの定義(CDK)が作るのは空の入れ物だけで、値は定義にも、リポジトリにも、テンプレートにも入れていません。値は、デプロイ後に人が自分の端末から入れます。

作ってみて分かった注意点が2つあります。

  • 差し替えは、すぐには効かない。 関数は読んだ値を一定時間キャッシュしているので、差し替えてもその時間ぶん古い値が使われる。ローテーションでは、古い鍵を少なくともその時間は有効にしておく必要がある
  • テンプレートから参照する形は、差し替えを拾わない。 デプロイ時に値を解決する参照は、参照の文字列が変わらない限り、値が変わっても更新されない。差し替えのたびに、参照先の版を明示してデプロイし直している
確認 — ここまで読めたか

Dockerfile で .env を COPY し、依存関係のインストールに使ったあと、次の RUN で rm .env しました。できあがったイメージに鍵は残りますか?

まず選ぶ(解答例は a〜d の記号で説明します)

演習 — まず自分で判断する

解説を読む前に、まず自分で判断してみてください。ここで一度詰まっておくと、 次の節の判断軸が「なるほど」ではなく「そう来たか」に変わります。

演習 — 設計判断を問う

サーバーレスの API で、秘密情報をどこにどう置きますか?

与えられた条件
  • 扱う秘密情報は、決済サービスの API キー、DB のパスワード、メール配信サービスの API キー
  • インフラは IaC で定義し、リポジトリに入っている
  • 環境は開発・検証・本番の3つ
  • 開発者は5人で、全員が本番の管理画面を閲覧できる
  • 監査の要件で、鍵は年1回以上差し替える必要がある
この軸で考える
  • · 値が見える人と経路を、どこまで絞れるか
  • · 差し替えのたびに、何を変更しデプロイする必要があるか
  • · 1本の鍵が漏れたとき、どこまで被害が及ぶか
まず選ぶ(解答例は a〜d の記号で説明します)

演習 — 説明できるか

誤ってコミットした API キーを「削除コミットを積んだので対応済みです」と報告してきた後輩に、何が足りないかを伝えてください

与えられた条件
  • 相手は入社1年目で、すぐに自分から報告してくれた
  • リポジトリは非公開
  • キーは本番の決済サービスのもの
  • 責めずに、次の一手を一緒に進めたい

読み終わりましたか?

読了にすると、これを前提とする記事がロードマップで開放されます。

この記事を前提にしている記事