セキュリティ●●●○○

シークレット管理

シークレットかんり

APIキーやパスワードなどの秘密情報を安全に保管・配布・ローテーションする仕組み。

概要

シークレット管理は、APIキー・データベースのパスワード・暗号化鍵・証明書の秘密鍵といった「秘密情報(シークレット)」を、安全に保管し、必要とするアプリケーションにだけ配布し、定期的に更新(ローテーション)するための仕組みと運用の総称です。アプリケーションが外部サービスや他システムと連携するかぎり、シークレットは必ずどこかに存在します。問題は「持つかどうか」ではなく「どう持つか」です。

コードの中に直接書く、設定ファイルに置く、環境変数で渡す、専用のシークレットマネージャから実行時に取得する — 置き場所の選択肢はいくつもあり、それぞれに漏えいのリスクと運用コストのトレードオフがあります。開発チームの規模が小さいうちは雑に扱っても事故が表面化しにくいのですが、リポジトリの公開・メンバーの入れ替わり・システムの増殖とともに、シークレットは「一番漏れやすく、漏れたとき一番痛い情報」になっていきます。

なぜ生まれたか

典型的な事故の形は昔から変わりません。開発中に「とりあえず動かすため」にAPIキーをソースコードへハードコードし、そのまま Git にコミットしてしまう。リポジトリが公開された瞬間、あるいは private リポジトリでも退職者のクローンや漏えいしたアカウント経由で、キーは第三者の手に渡ります。厄介なのは、Git は履歴をすべて保持するため、後からコードを消しても過去のコミットにシークレットが残り続けることです。実際、公開リポジトリを常時クロールしてAPIキーを収集するボットは数分単位で新規コミットを走査しており、「うっかりpushしたAWSキーが数分で悪用され高額請求が発生した」という報告は珍しくありません。

この反省から Twelve-Factor App は「設定はコードから分離し環境変数に置く」と説きましたが、環境変数にも限界があります。誰がいつ取得したかの記録が残らず、更新には再デプロイが必要で、値は平文のまま各所に散らばります。「散らばったシークレットを一箇所に集め、暗号化して保管し、取得を認可と監査の下に置き、機械的に更新できるようにする」— この要求に応えるために、HashiCorp Vault やクラウド各社のシークレットマネージャに代表される専用基盤が生まれました。

詳細

進化の系譜 — ハードコードから集中管理へ

シークレットの置き場所は、おおまかに三段階で進化してきました。それぞれ前の段階の弱点を潰しています。

ハードコードコードに直接記述リポジトリごと流出履歴に残り続ける変更にはコード修正環境変数コードと設定を分離ログやエラー画面に露出取得の記録が残らない更新に再デプロイが必要シークレットマネージャ暗号化して集中保管認可つき取得と監査ログ自動ローテーション動的シークレット発行進化の方向「値をどこに書くか」から「誰が・いつ・何を取得できるかを制御する」へシークレットは静的な文字列から、発行・失効を管理されるライフサイクルへ
シークレットの置き場所の進化 — 各段階が前の段階の弱点を解消する

第一段階のハードコードは論外に見えますが、いまでも新規の漏えい原因の上位を占めます。第二段階の環境変数は Twelve-Factor App 以降の標準で、コードとシークレットの分離という大きな前進でした。しかし環境変数はプロセスの属性として平文で保持されるため、エラーレポートツールが環境変数ごとダンプしてログに流出する、同一マシン上の他プロセスから覗かれる、子プロセスに意図せず継承される、といった露出経路が残ります。

第三段階のシークレットマネージャ(HashiCorp Vault、AWS Secrets Manager、Google Secret Manager など)は、シークレットを暗号化して一箇所に保管し、アプリケーションは実行時に認証つきのAPIで取得します。これにより「誰が・いつ・どのシークレットを取得したか」が監査ログに残り、値の更新は保管庫側の一回の操作で済み、漏えいが疑われたときは取得履歴から影響範囲を特定できます。

動的シークレットとローテーション

シークレットマネージャの本領は、静的な値の保管庫にとどまらない点にあります。Vault が広めた「動的シークレット」は、アプリケーションが要求した時点でデータベースの一時ユーザーなどを生成し、TTL(有効期限)付きで貸し出す方式です。使い終われば自動失効するため、「漏れても短時間しか使えない」状態を作れます。これは OAuth が短命なアクセストークンで被害を時間的に限定する発想と同じで、現代のシークレット管理は「絶対に漏らさない」ではなく「漏れても被害が小さい」設計へ軸足を移しています。

静的なシークレットについても、定期的なローテーションを機械化することが重要です。手動運用のローテーションは「変えると何かが壊れるかもしれない」という恐怖で先送りされ、結果として同じキーが数年使われ続けます。新旧の値を一時的に併存させて切り替える手順を自動化しておけば、ローテーションは怖い作業ではなく日常の営みになります。

CI/CD と実行環境への注入

シークレットが最も動く場所が CI/CD パイプラインです。デプロイにはクラウドの認証情報が、テストには外部APIのキーが要ります。GitHub Actions などのCIサービスはシークレットストアを内蔵しており、ワークフローのログ上で値をマスクしてくれますが、echo で加工した値まで守ってくれるわけではありません。より進んだ形として、CIジョブに対して OIDC で短命トークンを発行し、長期キーをCIに一切置かない方式が広まっています。実行環境側では、Kubernetes の Secret リソースや、コンテナ起動時にシークレットマネージャから取得して環境変数やファイルとして注入するサイドカー方式が定番です。IaC のコードにも値そのものは書かず、参照だけを書くのが原則です。

漏えいに備える — 検知と失効の手順

どれだけ整備しても、漏えいの可能性はゼロになりません。防御側として用意しておくべきは三つです。第一に検知: git-secrets や Gitleaks のようなスキャナをコミット前フックとCIに仕込み、シークレットらしき文字列の混入を機械的に止めます。第二に失効手順: 「このキーが漏れたら、どこで無効化し、何を再発行し、どのシステムに再配布するか」を平時に文書化しておきます。漏えい対応で最悪なのは「このキーを止めたら何が壊れるか分からないので止められない」という状態です。第三に監査: シークレットマネージャの取得ログを監視し、普段と違う取得元・取得量を異常として扱います。シークレットの一覧と持ち主が把握できていること自体が、最大の防御力になります。