Dev Study
AWSサービスの内部原理 コース

64. 封筒暗号は現場で動いている — S3 SSE-KMSとEBS暗号化の内側

S3編で「鍵を包む鍵」として学んだエンベロープ暗号化が、SSE-KMSとEBS暗号化という2つの現場で実際にどう動くのかを図で追っていきます。

① PutObject:S3はKMSにデータキーを作らせ、平文キーは使い捨てる

S3(PutObject受信)オブジェクト本体を受け取った
② 平文データキー + 暗号化データキー を返す
AWS KMSデータキーを生成し、KMS鍵で暗号化
③ 平文データキーでAES暗号化
オブジェクト本体この平文キーで暗号化される
④ 使い終わったら即メモリから消す
平文データキーS3の中に残さない
暗号化データキーオブジェクトのメタデータとして保存
包まれたまま同居する
  • 公式の手順:S3が平文データキーと「KMS鍵で暗号化されたコピー」を要求 → KMSが生成して両方返す → S3がデータで暗号化し「使用後できるだけ早く平文キーをメモリから削除」 → 暗号化データキーをメタデータとして暗号化データと一緒に保存
  • S3編の封筒暗号レッスンで見た「データ鍵を包んで保存する」構造そのもの。違いは、包む鍵(ルート鍵)の管理をAWS KMSに委ねている点
  • 根拠: https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingKMSEncryption.html (SSE-KMS encryption workflow)

SSE-KMSのアップロードは、S3がKMSにデータキーを1本作らせ、その平文キーでオブジェクトを暗号化し、平文キーは即座にメモリから捨てる。手元に残るのは「包まれた(暗号化された)データキー」だけで、それをオブジェクトのメタデータとして同居させる。

② GET:包まれたデータキーをKMSに戻して解いてもらう

S3(GET受信)メタデータから暗号化データキーを取り出す
② 同じKMS鍵で復号し、平文データキーを返す
AWS KMSDecrypt
③ 平文データキーでオブジェクトを復号
暗号化オブジェクト本体→ 平文に戻る
平文データキー復号が済んだら再び即メモリから削除
  • だから必要なKMS権限が非対称になる:アップロードには kms:GenerateDataKey、ダウンロードには kms:Decrypt(マルチパートアップロードは両方必要)。DVA頻出の落とし穴
  • GET/HEADで暗号化リクエストヘッダを送るとHTTP 400 Bad Requestになる(鍵はメタデータ側にあり、リクエストで渡すものではない)
  • 根拠: https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingKMSEncryption.html (Permissions / decryption workflow)

ダウンロードは逆再生で、S3が保存済みの暗号化データキーをDecryptに渡し、KMSが同じKMS鍵で平文に戻す。暗号化にはGenerateDataKey、復号にはDecrypt——権限が2つに割れているのは、封筒を「作る」操作と「開ける」操作が別だからだ。

③ Bucket Keys:KMSリクエストコストを最大99%削減する『鍵のキャッシュ』

S3 Bucket KeyKMSが一度だけ生成するバケット専用の中間キー
一定時間だけS3内で使い回す
オブジェクトA用データキー
オブジェクトB用データキー
オブジェクトC用データキー
KMSへのリクエスト中間キーの有効期間中はS3内で完結し、オブジェクトごとには発生しない
  • Bucket Keysなし:オブジェクト1個ごとにKMSを叩く。Bucket Keysあり:S3からKMSへのリクエストトラフィックが減り、KMSリクエストコストを最大99%削減(公式表現は「reduce your AWS KMS request costs by up to 99 percent」)
  • やっていることは鍵の階層をもう1段足すこと=鍵のキャッシュ。頻繁なアクセスを手前の階層で吸収するキャッシュの原理を、暗号鍵に適用した版
  • 根拠: https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingKMSEncryption.html (Amazon S3 Bucket Keys / "request costs by up to 99 percent")

S3 Bucket Keysは、KMSに一度だけバケットレベルの中間キーを作らせ、それをS3内で一定時間使い回してオブジェクトごとのデータキーを生成する。KMSへのリクエストが減り、KMSリクエストコストは最大99%削減——正体は「鍵階層をもう1段足したキャッシュ」で、頻繁なアクセスを手前で吸収するキャッシュの原理の暗号鍵版だ。

④ encryption context:どの鍵をどこで使うかをKMSに縛る

Bucket Keys なしencryption context = オブジェクトARN
arn:aws:s3:::bucket/key
Bucket Keys ありencryption context = バケットARN
arn:aws:s3:::bucket
AWS KMSencryption context を追加認証データ(AAD)として使う
  • デフォルトのencryption contextは、Bucket Keys無効ならオブジェクトARN、Bucket Keys有効ならバケットARN。CloudTrailのログにも aws:s3:arn として現れ、どのARNがどの鍵で処理されたか追跡できる
  • Bucket Keysで単位がオブジェクトからバケットに繰り上がるのは、③で見た中間キーがバケット単位だから。contextの粒度も自然にバケットへ揃う
  • 根拠: https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingKMSEncryption.html (Encryption context)

encryption contextは「この鍵はこのARNのために使う」という条件をKMSに追加認証データ(AAD)として渡す仕組みで、暗号化時と復号時で一致しなければ復号は失敗する。デフォルト値はBucket Keys無効ならオブジェクトARN、有効ならバケットARN——中間キーの単位がそのままcontextの粒度になる。

⑤ EBS:ボリュームごとに鍵を作り、アタッチで初めて解く

EBSボリューム作成平文キーは受け取らず、暗号化データキーだけ要求
② 暗号化データキーをボリュームメタデータと共に保存
暗号化データキーボリュームに貼り付いて保管される
③ アタッチ時に CreateGrant + Decrypt で平文キーを取得
EC2 がボリュームをアタッチ初めてここで鍵を解く
Nitroハードウェア平文データキーをメモリに置き、ディスクI/Oを暗号化
アタッチ中は常駐
  • S3との違い:EBSは作成時点では平文キーが要らないので GenerateDataKeyWithoutPlaintext を使う。暗号化データキーだけを受け取り、ボリューム情報と一緒に保存する
  • アタッチのタイミングでEC2がKMSに CreateGrant(この操作を許可する一時的な委任)を出し、Decryptで平文データキーを取得。それをNitroハードウェアのメモリに置いてディスクI/Oを暗号化する
  • CreateGrantは、KMSが「誰がこの鍵をこの用途で使ってよいか」を一時的に委任する仕組み。エンベロープ暗号化の鍵を、実際に使う現場(EC2のNitro)へ橋渡ししている
  • 根拠: https://docs.aws.amazon.com/ebs/latest/userguide/how-ebs-encryption-works.html

EBSはボリュームごとにデータキーを作るが、作成時は平文キーを受け取らないGenerateDataKeyWithoutPlaintextを使い、暗号化データキーをボリュームメタデータと共に保存する。平文キーが登場するのはアタッチの瞬間だけ——EC2がCreateGrantとDecryptで鍵を解き、Nitroハードウェアのメモリに置いてディスクI/Oを暗号化する。封筒暗号のデータキーが「使われる現場」がここだ。

⑥ デタッチと鍵の無効化:データキーはKMS鍵に依存している証拠

デタッチ平文データキーをNitroのメモリから削除
ボリュームが離れたら鍵も消える
次にアタッチしようとすると…
KMS鍵を無効化暗号化I/OはKMS鍵ではなくデータキーで行っているため
再アタッチ失敗暗号化データキーをDecryptできない
KMS鍵を使える状態に戻すまで使えない
  • アタッチ中はデータキーで暗号化しているので、KMS鍵を無効化しても即座には影響しない。だがデタッチでNitroから平文データキーが消えると、次のアタッチは暗号化データキーを復号できず失敗する
  • これは「暗号化データキーはKMS鍵がないと解けない」という依存関係の直接の証拠。データキーはKMS鍵にぶら下がっている
  • 根拠: https://docs.aws.amazon.com/ebs/latest/userguide/how-ebs-encryption-works.html (How unusable KMS keys affect data keys)

デタッチするとNitroのメモリから平文データキーが消える。アタッチ中はデータキーで暗号化しているのでKMS鍵を無効化しても即座には止まらないが、一度デタッチすると次のアタッチは暗号化データキーを復号できず失敗する。データキーがKMS鍵に依存している——エンベロープ暗号化の二段構造が、そのまま可用性の依存関係として表れる。

公式ドキュメントで詳しく ↗