コード内のAPIキーとシークレットを安全に管理する方法
コードをコミットしてGitHubにプッシュし、そのまま忘れてしまう。数日後、あなたのAPIキーが公開リポジトリからスクレイピングされ、クラウドの請求が数千ドルに膨らんでいることに気づく。これは珍しい例外的なケースではなく、現代の開発において最も一般的で高くつくセキュリティミスの一つです。ソースコードにハードコードされたシークレットは攻撃者への贈り物であり、驚くほど簡単に回避できます。
ハードコードされたシークレットが危険な理由
APIキー、データベースパスワード、プライベートトークンをコードに直接埋め込むと、誰がそれを見られるかの制御を失います。ソースコードは移動します。クローンされ、フォークされ、Dockerイメージにコピーされ、チャットアプリに貼り付けられ、時には誤って公開されることもあります。シークレットがバージョン管理に入ると、たとえ後のコミットで削除しても、Gitの履歴に永遠に残ります。
攻撃者は公開リポジトリを積極的にスキャンし、キーのようなパターンを探します。自動化されたボットは漏洩したキーを数分以内に見つけて悪用できます。プライベートリポジトリであっても、ハードコードされたシークレットは最小権限の原則に違反します。読み取りアクセス権を持つすべての開発者が自動的に本番環境の認証情報を手に入れてしまうのです。
ルール #1: シークレットを絶対にハードコードしない
当たり前に聞こえますが、これが基本です。最初のステップはソースファイルからあらゆるシークレットを削除することです。これには sk_live_... のような明白な文字列だけでなく、接続文字列、秘密鍵、Webhook署名シークレットも含まれます。
代わりに、コードは実行時に環境からシークレットを読み取るべきです。以下はPythonの簡単な例です:
import os
api_key = os.environ.get("PAYMENT_API_KEY")
if not api_key:
raise RuntimeError("PAYMENT_API_KEY is not set")
Node.jsでは process.env.PAYMENT_API_KEY を使用します。Goでは os.Getenv("PAYMENT_API_KEY") です。パターンは普遍的です:コードはシークレットが外部から提供されることを期待します。
環境変数を安全に使用する
環境変数は大きな改善ですが、万能薬ではありません。エラーレポート、デバッグログ、プロセス一覧を通じて漏洩する可能性があります。以下のプラクティスに従ってください:
- 環境変数を絶対にログに出力しない。 エラーハンドラで
process.envやos.environをダンプするのは避けましょう。 .envファイルはローカル開発にのみ使用する。 初日から.envを.gitignoreに追加しましょう。- プレースホルダー値を含む
.env.exampleファイルを提供することで、新しい開発者が何を設定すべきか分かります。 - 起動時に必須のシークレットを検証する。 キーが欠けている場合は、本番環境で後からクラッシュするのではなく、すぐに失敗させましょう。
ローカル開発では、python-dotenv やNode.jsの dotenv のようなライブラリを使えば、何もハードコードせずに .env ファイルを簡単に読み込めます。ただし、そのファイルは絶対にコミットしてはいけないことを覚えておきましょう。
本番環境向けの集中型シークレットマネージャー
環境変数は小規模なプロジェクトではうまく機能しますが、多くのサービス、複数の環境、監査の必要性があると扱いにくくなります。専用のシークレットマネージャーは、シークレットを保存時に暗号化し、細かいポリシーでアクセスを制御し、監査証跡を提供することでこれらの問題を解決します。
一般的なオプションは以下の通りです:
- HashiCorp Vault – セルフホスト型で柔軟性が高く、動的シークレットをサポート。
- AWS Secrets Manager – AWSサービスとのネイティブ統合、自動ローテーション。
- Google Secret Manager – GCP向けの同様のサービス。
- Azure Key Vault – Microsoft Azure環境向け。
- Doppler、Infisical、1Password Secrets Automation – クロスプラットフォームのSaaSオプション。
アプリケーションは起動時またはオンデマンドで、多くの場合SDKを使用してマネージャーからシークレットを取得します。これによりシークレットの保存がコードから分離され、再デプロイせずに認証情報をローテーションできます。
CI/CDパイプラインにおけるシークレット
ビルドとデプロイのパイプラインにも、レジストリのパスワードやデプロイトークンなどのシークレットが必要です。ほとんどのCIシステム(GitHub Actions、GitLab CI、CircleCI)は暗号化されたシークレットストレージを提供しています。パイプライン設定ファイルにシークレットを書くのではなく、これらの機能を使用しましょう。
例えば、GitHub Actionsではリポジトリ設定でシークレットを定義し、次のように参照します:
steps:
- name: Deploy
run: ./deploy.sh
env:
API_TOKEN: ${{ secrets.DEPLOY_API_TOKEN }}
フォークからのプルリクエストには注意が必要です。デフォルトではフォークPRによってトリガーされたワークフローにシークレットは渡されません。これは良いことです。ログにシークレットをエコーしないでください。CIシステムがサポートしている場合はマスクしましょう。
シークレットを定期的にローテーションする
完璧な保存方法でも、シークレットは他のチャネルを通じて漏洩する可能性があります:侵害されたラップトップ、設定ミスのあるロギングサービス、退職する従業員などです。ローテーションは露出の期間を制限します。
機密性に基づいてローテーションスケジュールを設定しましょう。高価値のキー(決済ゲートウェイ、管理API)は30〜90日ごとにローテーションするかもしれません。低リスクのキーはもっと頻度を下げてもよいでしょう。可能な場合はローテーションを自動化しましょう。AWS Secrets ManagerとVaultはデータベース認証情報を自動的にローテーションできます。
ローテーション時には、移行期間中にアプリケーションが複数の有効なシークレットを処理できるようにしてください。一般的なパターンは、短期間だけ古いシークレットと新しいシークレットの両方を受け入れ、その後古い方を無効化することです。
漏洩を検出して防止する
予防は治療に勝りますが、検出は安全網です。コミット前にシークレットをスキャンするためにpre-commitフックを使用しましょう。git-secrets、trufflehog、gitleaks などのツールが偶発的なコミットを捕捉できます。
また、Gitホスティングプラットフォーム(GitHub、GitLab、Bitbucketはいずれも提供)でシークレットスキャンを有効にしましょう。もしシークレットがすり抜けてしまったら、すぐに取り消してローテーションしてください。コミットを削除するだけでは不十分です。リモートリポジトリに触れた瞬間にシークレットは侵害されたとみなしてください。
シークレット保存アプローチの比較
| 方法 | 最適な用途 | リスク |
|---|---|---|
| 環境変数 | 小規模アプリ、ローカル開発 | ログやプロセス検査による漏洩 |
.env ファイル |
ローカル開発 | 偶発的なコミット、暗号化なし |
| シークレットマネージャー | 本番環境、チーム | 複雑さ、依存関係の追加 |
| CI/CDシークレットストア | ビルドとデプロイのパイプライン | パイプラインスコープに限定 |
FAQ
プライベートリポジトリにシークレットを保存してもいいですか?
いいえ。プライベートリポジトリでも、読み取りアクセス権を持つ多くのユーザーや統合があります。シークレットはフォーク、CIログ、侵害されたアカウントを通じて漏洩する可能性があります。プライベートコードであっても、常に環境変数またはシークレットマネージャーを使用してください。
誤ってシークレットをコミットしてしまったらどうすればいいですか?
すぐにシークレットを取り消してローテーションしてください。コミットを削除したり履歴を書き換えたりするだけでは不十分です。シークレットはすでにキャッシュまたはクローンされている可能性があります。侵害されたものとして扱い、交換してください。
環境変数は本番環境で十分に安全ですか?
ハードコードよりはましですが、大規模な本番環境には理想的ではありません。環境変数はクラッシュダンプ、デバッグエンドポイント、プロセス一覧で露出する可能性があります。本番環境では、アクセス制御と監査を備えた専用のシークレットマネージャーを使用してください。
機密性のない設定を含むJSON設定ファイルを迅速にフォーマットまたは検証する必要がある場合、JSON Formatter がデプロイを壊す前に構文エラーを見つけるのに役立ちます。ただし、実際のシークレットをオンラインツールに貼り付けないでください。