【実務・中級編】 シークレット管理ツール(HashiCorp Vault等)の統合 – インフラ・ネットワーク & クラウドセキュリティ防御ガイド

環境変数にDBパスワードを置いていないか?その「安易な設計」がインフラを焼き払う

インフラエンジニアや開発者の諸君、お疲れ様。今日もどこかのサーバーで「設定ファイルにハードコードされたAPIキー」や「環境変数に晒されたDBパスワード」が、攻撃者の格好の餌食になっている。

正直に言おう。環境変数(ENV)に機密情報を埋め込む時代は終わった。

「でも、DockerのComposeファイルに書くのが一番手っ取り早いし……」という言い訳は、フォレンジック調査の現場では通用しない。今回は、なぜ環境変数が危険なのか、そしてHashiCorp Vaultを活用して「そもそも認証情報を永続化させない」という、一歩先を行くセキュリティ実装について解説する。

—

なぜ「環境変数」は攻撃者の宝の山なのか?

攻撃者がサーバーへの足がかり(RCEやLFIなど)を得たとき、最初に行うのは何だと思う?
printenv や env コマンドを叩くことだ。

攻撃のシナリオ(PoC)

1. 侵入: アプリケーションの脆弱性(ファイルアップロードやコマンドインジェクション)を突き、Webシェルを設置。
2. 偵察: cat /proc/1/environ を実行。プロセスID 1(コンテナのメインプロセス)の環境変数は、漏れなく取得可能だ。
3. 奪取: DBの接続文字列、AWSのアクセスキー、S3のバケット情報が平文で手に入る。
4. 横展開: 奪ったキーでクラウドインフラを操作し、バックアップを削除、あるいは暗号化して身代金を要求する。

これが現在の「クラウドネイティブな脅威」の標準的な攻撃プロセスだ。環境変数は「プロセスが知っている情報」であり、プロセスを乗っ取れば100%漏洩する。

—

HashiCorp Vaultによる「短命な認証情報」の真髄

我々が目指すべきは、「静的なパスワードを管理しない」世界だ。
HashiCorp Vaultを使えば、アプリケーションが必要な瞬間にだけ一時的な認証情報を生成し、使い終われば自動的に無効化される。万が一、認証情報が漏洩しても、それは既に期限切れのゴミ同然というわけだ。

PythonによるVault連携実装例

以下は、環境変数を一切使わず、Vaultから動的にDBの認証情報を取得する実装パターンだ。

import hvac
import psycopg2

def get_db_connection():
    # Vaultクライアントの初期化(トークンはK8sのServiceAccount等から自動注入)
    client = hvac.Client(url='https://vault.internal:8200')

    # Vaultのデータベースシークレットエンジンから一時的な認証情報を取得
    # この認証情報は、設定したTTL(例: 1時間)で自動的に削除される
    read_response = client.secrets.database.generate_credentials(
        name='my-postgresql-role',
        mount_point='database'
    )

    username = read_response['data']['username']
    password = read_response['data']['password']

    # 接続を確立
    return psycopg2.connect(
        dbname="production_db",
        user=username,
        password=password,
        host="db-cluster.internal"
    )

# 使うときはこうする
try:
    conn = get_db_connection()
    # 処理実行...
finally:
    conn.close()

このコードのポイントは、コード内に認証情報が一切存在しないことだ。仮にこのソースコードがGitHubに流出しても、攻撃者は何も得られない。

—

クラウド側の防壁:IAMポリシーの最適化

Vaultと連携する場合、Vault自体をどう守るかが重要になる。AWS上で運用するなら、Vaultが持つIAMロールには「必要最低限の権限」のみを付与する。

以下は、Terraformで設定する際の「Vault用の最小権限ポリシー」の概念例だ。

# Vaultが必要なのは「DBの認証情報を生成する権限」と「K8sの認証検証」のみ
resource "aws_iam_policy" "vault_policy" {
  name = "vault-secrets-engine-policy"

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = [
          "rds-db:connect" # RDSへの接続許可のみに絞る
        ]
        Effect   = "Allow"
        Resource = "arn:aws:rds-db:region:account-id:dbuser:db-id/*"
      }
    ]
  })
}

—

現場のエンジニアへ:明日からやるべきこと

明日からすぐに「環境変数の全廃」は難しいかもしれない。だが、以下のステップで着実に要塞化を進めてほしい。

1. 棚卸し: printenv を叩いて、何が露出しているかを確認する。
2. シークレットの分離: まずはGitのレポジトリから全てのパスワードを削除し、VaultやAWS Secrets Managerへ移行する。
3. 動的生成への移行: 静的なパスワード管理から、DBロールを動的に発行する仕組みへ切り替える。
4. 不要サービスの停止: サーバーの netstat -tulpn を確認し、自分たちが使っていないポート(古いSSHエージェントや管理用APIなど)は即座に停止、あるいはファイアウォールで遮断する。

セキュリティとは「一度作れば終わり」ではない。攻撃者は常に「管理者の油断」という脆弱性を狙っている。「認証情報は常に疑え、そして短命にせよ」。 これが、インシデントに巻き込まれない唯一のプロの哲学だ。

分からなければ、いつでもコードを見せてくれ。厳しくレビューしてやるから。

コメント

タイトルとURLをコピーしました