【実務・中級編】 AWS Lambdaの実行環境におけるIAMロールの分離 – 暗号理論・認証基盤 & エンドポイントセキュリティ防御ガイド

AWS Lambdaの「万能IAMロール」は死を招く:最小権限の原則を現場で叩き込む

「Lambdaなんてただのスクリプトだし、とりあえず AdministratorAccess 付与して動かしておけばいいか」

もし君のチームでそんな会話が聞こえてきたら、即座に耳を塞いでそのPCをシャットダウンさせるべきだ。セキュリティの現場で「とりあえず」という言葉は、爆弾の安全装置を外すのと同じ意味を持つ。

今日は、暗号理論の堅牢性(AESやRSA)以前の、現代のクラウドインフラにおける「侵入経路の遮断」について、LambdaのIAM分離という観点から語ろう。

1. なぜ「万能ロール」が攻撃者のパラダイスなのか

攻撃者は、Lambdaのコード内に存在する小さな脆弱性(例えば、依存ライブラリのRCE:リモートコード実行)を見逃さない。一度Lambdaの実行環境を掌握されたらどうなるか?

権限が過剰なIAMロールが付与されていた場合、攻撃者はそのLambdaから:

  • S3バケットの全データを窃取する
  • DynamoDBの顧客情報をダンプする
  • 他のEC2インスタンスを乗っ取るためのIAMクレデンシャルを生成する

これが「水平展開(Lateral Movement)」だ。Lambda一つを侵されただけで、インフラ全体が芋づる式に崩壊する。これを防ぐ唯一の防御策が「関数ごとの権限分離」である。

2. 楕円曲線暗号(ECC)を語る前に、IAMポリシーの「暗号」を解く

ここで少し暗号の話を挟もう。TLS通信で使われるRSAやECC(楕円曲線暗号)は、通信経路の「完全性」と「機密性」を保証する。しかし、LambdaのIAMは、いわば「操作の論理境界」を決定する鍵だ。

AESでデータを暗号化していても、LambdaがS3のs3:GetObject権限を持っていれば、攻撃者はその暗号化されたファイルをそのまま盗み出せる。鍵(IAM)が盗まれていれば、どれほど強力な暗号も無意味だ。だからこそ、IAMの設計は「最も強固な鍵管理」として扱うべきなんだ。

3. 実践:最小権限のIAMポリシー設計

例えば、S3の特定のフォルダにログを出力するだけのLambdaなら、他の権限は一切不要だ。以下のIAMポリシーをテンプレートとして参考にしてほしい。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowSpecificS3PutOnly",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject"
            ],
            "Resource": "arn:aws:s3:::my-secure-log-bucket/logs/*"
        }
    ]
}

このポリシーのポイントは、Actionをs3:PutObjectに限定し、Resourceも特定のパスまで絞り込んでいることだ。もし攻撃者がこのLambdaを乗っ取ったとしても、既存のログを読み取る(s3:GetObject)ことさえできない。

4. 【コード実装】Node.jsにおける環境変数とSDKの分離

Lambdaのコード内でSDKを使う際も、グローバルに認証情報を渡さず、特定の権限を意識した実装が必要だ。

// AWS SDK v3 を使用したセキュアな設計
const { S3Client, PutObjectCommand } = require("@aws-sdk/client-s3");

// クライアントを関数外で初期化することで、実行環境の再利用時にセッションを維持
// ただし、権限はIAMロールにより「そのLambdaが実行可能な範囲」に物理的に制限されている
const s3Client = new S3Client({ region: "ap-northeast-1" });

exports.handler = async (event) => {
    try {
        const params = {
            Bucket: "my-secure-log-bucket",
            Key: `logs/${Date.now()}.json`,
            Body: JSON.stringify(event)
        };
        
        // 特定のバケットのみに書き込む最小限の操作
        await s3Client.send(new PutObjectCommand(params));
        
        return { statusCode: 200, body: "Logged successfully" };
    } catch (err) {
        // 攻撃者に詳細なエラーを返さない(情報漏洩の防止)
        console.error("Internal Error");
        return { statusCode: 500, body: "Internal Server Error" };
    }
};

5. 後輩エンジニアへ:明日からやるべきこと

1. IAMの棚卸し: AWSマネジメントコンソールを見て、「AdministratorAccess」や「PowerUserAccess」がついているLambdaがないか確認しろ。
2. IAM Access Analyzerの活用: どの権限が実際に使われているかを分析し、使われていない権限を削除する。
3. 環境変数の分離: データベースのパスワードやAPIキーをLambdaの環境変数に直書きしてはいけない。AWS Systems Manager Parameter StoreやSecrets Managerを使い、Lambdaには「読み取り専用」の権限だけを与える。

セキュリティとは、「完璧な防御」を目指すことではなく、「侵入された後の被害をいかに局所化するか」というデザインの問題だ。

君たちが書く一行のコード、設定する一行のJSONが、顧客の信頼を支えている。その重みを忘れず、明日のデプロイメントに臨んでほしい。質問があればいつでも来い。現場からは以上だ。

コメント

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