現場のエンジニア諸君、今日も深夜のデプロイお疲れ様。
コードを書いていて「ちょっとテストのためにDBパスワードを直書きして、あとで消せばいいや」と思ったことはないか? その「あとで」が、我々のインシデント対応の悪夢の始まりだ。
今日は、IaC(Infrastructure as Code)時代において、なぜシークレット管理が「努力目標」ではなく「生存戦略」なのか、そしてどうやって泥臭く防ぐかを解説する。
—
1. なぜ「ハードコード」が命取りになるのか(PoC的視点)
攻撃者は、GitHubの公開リポジトリを24時間監視するボットを走らせている。君が git push を叩いてから、数秒後にはそのAWSのAccess Keyは攻撃者の手に渡り、マイニング用のEC2インスタンスが数十台立ち上がるだろう。
攻撃者の思考プロセス
1. GitHub Dorking: gh-ost や trufflehog を使って、リポジトリ履歴から AWS_ACCESS_KEY_ID っぽい文字列を抽出。
2. 権限昇格: 盗んだキーで sts get-caller-identity を実行し、権限を確認。
3. IAM権限の悪用: iam:CreateAccessKey や s3:ListBucket を使い、バックアップデータや環境変数を窃取。
一度コミット履歴に刻まれたシークレットは、リポジトリを削除しても「残る」。これは呪いと同じだ。
—
2. コミット前の「門番」を導入する
「人間はミスをする」という前提でシステムを組む。Gitのフックを利用して、コミット前にシークレットを弾くのが鉄則だ。
git-secrets の設定
まずは、ローカル環境に git-secrets を仕込もう。
# インストール(Mac環境の例)
brew install git-secrets
# リポジトリごとの設定
git secrets --install
git secrets --register-aws --global
# コミット時にAWSクレデンシャルが含まれていたらエラーにする設定
git secrets --add '(?i)aws_access_key_id|aws_secret_access_key'
これで、うっかりパスワードを含んだまま git commit しようとすると、ターミナルに警告が走り、コミットが強制終了される。
—
3. 実践:AWS Secrets Manager を使った動的シークレット管理
ハードコードを排除した後は、シークレットを「実行時に取得する」仕組みに置き換える必要がある。
Pythonでの実装例
環境変数すら信用せず、SDK経由でAPIから取得するのが最も安全だ。
import boto3
from botocore.exceptions import ClientError
def get_secret():
secret_name = "prod/db/credentials"
region_name = "ap-northeast-1"
# セッションを作成(IAMロールによる認証を推奨)
session = boto3.session.Session()
client = session.client(service_name='secretsmanager', region_name=region_name)
try:
# Secrets Managerからシークレットを取得
get_secret_value_response = client.get_secret_value(SecretId=secret_name)
except ClientError as e:
# ログを吐いて適切に終了する(エラーの内容を攻撃者に教えない工夫が必要)
raise e
return get_secret_value_response['SecretString']
# 使用例
db_password = get_secret()
print("安全に接続情報を取得しました。")
—
4. インフラ側で絶対にやるべきこと:IAM Roleの活用
コードにキーを埋め込む最大の理由は「認証情報の管理が面倒だから」だろう。しかし、EC2やECS、Lambdaを使っているなら、「IAMロール」をインスタンスに付与せよ。
IAMロールを使えば、AWS_ACCESS_KEY_ID といった静的なシークレットをプログラム内に一切持たなくて済む。AWS SDKは自動的に一時的な認証情報を取得してくれる。
TerraformでのIAMポリシー例(最小権限の原則)
必要なサービスにのみアクセスを許可する。
resource "aws_iam_policy" "secrets_access" {
name = "AppSecretsAccess"
description = "Secrets Managerへの読み取り専用アクセス"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = ["secretsmanager:GetSecretValue"]
Effect = "Allow"
# 特定のシークレットのみに限定する
Resource = "arn:aws:secretsmanager:ap-northeast-1:123456789012:secret:prod/db/credentials-*"
}
]
})
}
—
結論:セキュリティは「仕組み」で解決する
「注意してコードを書く」という精神論は、今の開発スピードでは通用しない。
1. コミット前: git-secrets で物理的に弾く。
2. CI/CD: TruffleHog をパイプラインに組み込み、過去のコミットも含めてスキャンする。
3. 本番環境: 環境変数すら使わず、AWS Secrets Manager と IAM Role で動的に注入する。
これを徹底するだけで、君のプロダクトの防御力は劇的に向上する。セキュリティは「誰かがやってくれるもの」ではない。コードを書く君自身が、システムを守る最後の砦なんだ。
さあ、明日からは「シークレットをコミットした」なんて恥ずかしい報告をチャットに流さないようにしよう。健闘を祈る。
コメント