Bucket KeyのKMSコスト削減に貢献する仕組み

AWS S3では、オブジェクトをKMSで暗号化する際に、Bucket Keyというオプションがあります。AWSの公式ドキュメントには、このBucket Keyを有効にするとコスト削減できると記載されています。また、その仕組みの概要の説明もあります。

S3 バケットキーを使用すると、バケットレベルのキーを使って EncryptGenerateDataKeyDecrypt オペレーションの AWS KMS リクエスト数を減らし、AWS KMS リクエストのコストを節約できます。この設計により、このバケットレベルのキーを利用する後続のリクエストは、AWS KMS APIリクエストを発生させたり、AWS KMSキーのポリシーに対してアクセスを検証したりすることはありません。

しかしここに2つの疑問が浮かび上がります。1つは、Bucket KeyとKMSのData Keyの違いはなんなのか、もう1つは、どこまでKMSのリクエストが削減されるかです。では、実際に検証して確認しましょう。

Bucket KeyとData Keyの違い

KMSを利用して暗号化・復号化する際に、基本的に2つの方法があります。

1つは、直接KMSが管理してるMaster Keyを使用する方法です。APIで言うと、KMSの「Encrypt」と「Decrypt」を使用する方法です。この方法は、データを暗号化・復号化するたびにKMSのAPIを呼び出す必要があるので、推奨していません。ただし、この方法ではキーマテリアルは絶対にKMSの外に出ることはないので、もっともセキュアな方法とも言えるでしょう。

もう1つは、いわゆるEnvelope Encryptionという方法です。「GenerateDataKey」というAPIが提供されており、平文のキーとそれをMaster Keyで暗号化したキーがセットでAPIの呼び出し元に返されます。このキーのことをData Keyと言います。基本的に平文のData Keyは、データを暗号化するのに使用したのち、直ちに破棄しなければなりません。平文のキーが一瞬だけメモリ上に存在するのですが、通常リスクが低いと考えられます。

Buckey KeyもData Keyと同様に、データを暗号化するためのキーで、仕組み上Data Keyとほぼ同じです。ただし、Bucket Keyはそのままデータの暗号化には使用しません。Bucket Keyを使用して、さらにData Keyを生成します。つまり、KMSが管理しているMaster Keyの役割を、Bucket Keyに移したわけです。

Bucket KeyによるKMS API呼び出し回数の削減効果

こちらを実際のAWS環境で検証してみました。まずはKMSでMaster Keyを作成しておきます。そして、Cloudshell環境でテスト用のファイルを20個作成します。

mkdir files
for i in {1..20}; do dd if=/dev/urandom of=./files/random_$i bs=1M count=1 &> /dev/null; done

比較用に、2つのS3バケットを作成し、それぞれBucket Keyを有効・無効にします。まずは1個目のバケット:

aws s3api create-bucket \
    --bucket keytest-20250305-a \
    --region ap-northeast-1 \
    --create-bucket-configuration LocationConstraint=ap-northeast-1

aws s3api put-bucket-encryption \
    --bucket keytest-20250305-a \
    --server-side-encryption-configuration '{
        "Rules": [
            {
                "ApplyServerSideEncryptionByDefault": {
                    "SSEAlgorithm": "aws:kms",
                    "KMSMasterKeyID": "4e28f70f-63d8-4f9d-ac3a-276a84cf541c"
                },
                "BucketKeyEnabled": true
            }
        ]
    }'

2個目のバケット:

aws s3api create-bucket \
    --bucket keytest-20250305-b \
    --region ap-northeast-1 \
    --create-bucket-configuration LocationConstraint=ap-northeast-1

aws s3api put-bucket-encryption \
    --bucket keytest-20250305-b \
    --server-side-encryption-configuration '{
        "Rules": [
            {
                "ApplyServerSideEncryptionByDefault": {
                    "SSEAlgorithm": "aws:kms",
                    "KMSMasterKeyID": "4e28f70f-63d8-4f9d-ac3a-276a84cf541c"
                },
                "BucketKeyEnabled": false
            }
        ]
    }'

そして、それぞれのバケットに対して20個のファイルをアップロードします。

aws s3 cp files/ s3://keytest-20250305-a/ --recursive
aws s3 cp files/ s3://keytest-20250305-b/ --recursive

そして、CloudTrailでのイベントを確認します。

aws cloudtrail lookup-events \
  --lookup-attributes AttributeKey=EventSource,AttributeValue=kms.amazonaws.com \
  --query "Events[].[EventId, EventName,EventTime]" \
  --output table

確認してみると、非常に興味深い結果になりました。

Bucket Keyをオフにしたバケットからは、20回の「GenerateDataKey」のAPI呼び出しが記録されていました。これは予想通りで、S3ではファイルごとに異なるData Keyを使用して暗号化するため、オブジェクトの数だけのGenerateDataKey APIコールをします。

一方で、Bucket Keyをオンにしたバケットからは、2回の「GenerateDataKey」のAPI呼び出しが記録されていました。さらにこのテストを何回か繰り返してみたところ、

  • 1回目のテスト:2回のGenerateDataKeyの呼び出し
  • 2回目のテスト:4回のGenerateDataKeyの呼び出し
  • 3回目のテスト:4回のGenerateDataKeyの呼び出し
  • 4回目のテスト:3回のGenerateDataKeyの呼び出し

私の予想では、1回ではないかと思っていました。なぜかというと、Buckey Keyは長期間同じものが使いまわされるわけではなく、有効期限があることを前から知っていましたが、20個のファイルをいっぺんにアップロードしたので同じBucket Keyが使われるのではないかと踏んでいました。

ただしよく考えてみると、20個のファイルをアップロードしたのですが、S3のAPIエンドポイントはきっと1台のサーバーではなく、複数台のサーバーで構成されています。AWSでは、暗号鍵をサーバーを跨いで転送されることがないので、きっと、1回目のアップロードは2台のサーバーによって処理され、2回目のときはたまたま4台によって処理された、という原因なのではないかと思います。

ここら辺はAWSの内部的な動作なので一般向けに公開されることはないでしょうが、覚えてほしいのはBucket Keyを使用しても、KMS APIの呼び出し回数が1になるというわけではないことです。ただし、それでもいっぺんにたくさんのファイルを読み書きする場合、Bucket Keyを有効にすることでかなりのKMSコスト削減を期待できるのではないかと思います。

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top