😎

Amazon S3 のサーバーサイド暗号 (encryption at rest)

2024/03/20に公開

参考)

概要

Amazon S3 にオブジェクトをアップロードすると、サーバーサイドで自動的に暗号化されます。2023/01 以降は、暗号化せずに保存することはできなくなりました。暗号方式には以下のものがあり、アップロード時に指定するか、指定しなかった場合はバケットの「デフォルトの暗号化」に設定した方式が使用されます。

  • SSE-S3: Server-side encryption with S3-managed keys
  • SSE-KMS: Server-side encryption with AWS KMS keys
  • DSSE-KMS: Dual-layer server-side encryption with AWS KMS keys
  • SSE-C: Server-side encryption with customer-provided keys

クライアントサイドの暗号化や通信の暗号化(encryption in transit)には触れません。

暗号方式の違い

SSE-S3: Server-side encryption with S3-managed keys

AWS 内部で管理されているキーで暗号化します。アクセスコントロールはできません。追加料金もかからず、デフォルトの暗号方式になっています。S3 へのアクセス権があれば勝手に復号されたオブジェクトにアクセスできるため、「物理ストレージには暗号化されて保存されている」という事実以上の意味はなさそうです。

  • キーへのアクセスや設定は不可、ローテーションは自動でおこなわれる
  • オブジェクトを public に公開できる

SSE-KMS: Server-side encryption with AWS KMS keys

SSE-KMS

AWS KMS を使用して暗号化します。AWS KMS 自体は 4 KB までのデータしか暗号化しかできないため、オブジェクトごとに異なる暗号鍵 (Data key) を生成して S3 上でオブジェクトを暗号化し、KMS で暗号化した暗号鍵を、暗号化したオブジェクトのメタデータとして保存します。

  • S3 用の AWS-managed key (aws/s3) または Customer-managed key を選択
  • アップロード/ダウンロード時に暗号鍵へのアクセス権も別途必要 (後述)
  • AWS KMS の料金が追加でかかり、API コール性能に対する上限が発生する (対策あり、後述)
  • マネージドサービスのログ等の送信先として使うことはできない
  • オブジェクトを public に公開することはできない

アップロード処理の流れ

ダウンロード処理の流れ

また S3 Bucket Key 機能を有効にすると、Data key を生成するための中間キーを生成することで、AWS KMS へのアクセスを減らすことができます。料金や性能のデメリットを極限まで減らしつつ KMS の恩恵を受けられるため、基本的に有効にしておくと良いでしょう。

S3 Bucket Key

(TODO: S3 Bucket Key はそれぞれのオブジェクトを暗号化した Data key を復号するのに必要になるため、ずっとどこかに残しておく必要があると思いますが、どのように保存・管理され、どの程度の間隔でローテーションされるのかはわかりませんでした。)

DSSE-KMS: Dual-layer server-side encryption with AWS KMS keys

米国政府によって定められたセキュリティ基準を満たす、二重層の暗号化をおこなう方式とのこと。使用する KMS key は SSE-KMS と変わらないため、アクセスコントロール的には何の違いもなさそうです。S3 Bucket Key も使用できず、追加料金もかかります。高いコンプライアンスルールが課せられる案件向けでしょうか。ご苦労様です。

SSE-C: Server-side encryption with customer-provided keys

暗号鍵の管理はクライアント側でおこない、暗号化/復号処理はサーバーサイドでおこなう方式です。S3 側で暗号鍵の管理はおこないません。

  • アップロード/ダウンロード時に暗号鍵を渡す
  • 暗号鍵を送信するため、HTTPS が必須
  • オブジェクトを public に公開することはできない

アップロード処理の流れ

ダウンロード処理の流れ

AWS KMS key へのアクセス権

SSE-KMS で暗号化する場合、S3 への権限だけでなく、KMS key へのアクセス権も必要になります。

Key policy に設定する場合 (以下の例は、アカウント内のどのユーザーでも暗号化/復号できるようになります。)

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "*"
            },
            "Action": [
                "kms:Encrypt",
                "kms:Decrypt",
                "kms:ReEncrypt*",
                "kms:GenerateDataKey*",
                "kms:DescribeKey"
            ],
            "Resource": "*",
            "Condition": {
                "StringEquals": {
                    "kms:ViaService": "s3.ap-northeast-1.amazonaws.com",
                    "kms:CallerAccount": "XXXXXXXXXXXX"
                }
            }
        }
    ]
}

IAM policy に指定する場合

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "kms:Encrypt",
                "kms:Decrypt",
                "kms:ReEncrypt*",
                "kms:GenerateDataKey*",
                "kms:DescribeKey"
            ],
            "Resource": "(KMS key の ARN)"
        }
    ]
}

暗号方式を強制する

アップロード時の暗号方式は、以下のヘッダで指定します。(参考: Put Object)

  • SSE-S3

    x-amz-server-side-encryption: AES256
    
  • SSE-KMS

    x-amz-server-side-encryption: aws:kms
    x-amz-server-side-encryption-aws-kms-key-id: (KMS キー ID)
    
  • DSSE-KMS

    x-amz-server-side-encryption: aws:kms:dsse
    x-amz-server-side-encryption-aws-kms-key-id: (KMS キー ID)
    
  • SSE-C

    x-amz-server-side-encryption-customer-algorithm: (暗号アルゴリズム)
    x-amz-server-side-encryption-customer-key: (暗号鍵)
    x-amz-server-side-encryption-customer-key-MD5: (暗号鍵のダイジェスト)
    

これらのヘッダを条件に、バケットポリシーに以下のように設定することで特定の暗号方式を強制することができます。ただしこの場合、バケットにデフォルトの暗号化を設定していても、アップロード側で指定しないと拒否されることには注意です。(参考: Bucket policy examples)

  • SSE-KMS を強制する

    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Deny",
                "Action": "s3:PutObject",
                "Principal": "*",
                "Resource": "arn:aws:s3:::my-bucket/*",
                "Condition": {
                    "StringNotEquals": {
                        "s3:x-amz-server-side-encryption": "aws:kms"
                    }
                }
            }
        ]
    }
    
  • SSE-C を強制する

    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Deny",
                "Action": "s3:PutObject",
                "Principal": "*",
                "Resource": "arn:aws:s3:::my-bucket/*",
                "Condition": {
                    "Null": {
                        "s3:x-amz-server-side-encryption-customer-algorithm": "true"
                    }
                }
            }
        ]
    }
    

Discussion