社内向けに書いた記事を転載。
SSE (サーバーサイド暗号化)
SSE-S3
S3が管理するキーでの、透過的暗号化。
- Pros
- 設定すればいいだけなのでラク
- 共通の鍵なので追加コストもなし
- Cons
- 透過的復号がされてしまうので、S3のコンソールでダウンロードしても復号されてしまう。クレデンシャル漏洩には無意味。
SSE-S3になっているかは、オブジェクトの「サーバー側の暗号化設定」の箇所で確認できる。
SSE-KMS
AWS-KMSの鍵での、透過的暗号化。
- Pros
- 設定すればいいだけなのでラク
- Cons
- 透過的復号がされてしまうので、S3のコンソールでダウンロードしても復号されてしまう。クレデンシャル漏洩には無意味。
- KMSから鍵を取ってくるので回数が増えると多少、コストに響く
確認方法はSSE-S3と同様。
SSE-C (カスタマーキーによるサーバーサイド暗号化)
ユーザーが管理する鍵をサーバーにオブジェクトと一緒に毎回アップロードしてサーバーサイドで暗号化する方式。
CSEに比べるとあまり変わらないが、暗号化、復号にユーザー側サーバーのリソースを使わないのが利点か。
以下の項目をパラメーターとしてS3にputする。
- sse_customer_algorithm
- AES256のみ
- sse_customer_key
- sse_customer_key_md5 (optional)
- なくても使えるけど、つけた方がいい
残念ながらPresigned URLは使えない。発行はできるが、エンドユーザーがアクセスするときにも固有のヘッダーが必要になるため、直接アップロード、ダウンロードなどには使えない。
SSE-C 以外のオブジェクトでは、署名付き URL を生成し、それをブラウザに直接貼り付けることで、たとえばデータにアクセスできます。
ただし、これは SSE-C オブジェクトには当てはまりません。署名付き URL に加えて SSE-C オブジェクトに固有の HTTP ヘッダーも含める必要があります。したがって、SSE-C オブジェクトの署名付き URL はプログラムでのみ使用できます。
https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/specifying-s3-c-encryption.html
AWSのサポートにも問い合わせたが、以下を満たす最高のソリューションは存在しないとのこと。
- 透過的復号をしない
- 署名付きURLが特別なヘッダーなしで使える
透過的復号に関しては、IAMの権限でGet-Objectを制限することで近い状態にはできるのでは、というアドバイスがあった。
Pros/Cons
- Pros
- 透過的復号がされないので、クレデンシャル漏洩に効果がある
- Cons
- Presigned URLが実質使えない
- コンソールから気軽にダウンロードできない
- させるべきでないならこれもProsになる
- ローカルで鍵の管理が必要
- RailsのCredentialsに入れてもいいしKMSから鍵を取得する方式にしてもいいので、それほどのConsでもない
CLIでの例
$ aws s3api put-object --bucket $BUCKET --key sse-upload --body sse-c.txt --sse-customer-algorithm AES256 --sse-customer-key { "ETag": "\"3a2def088089b2d7d7aee1xxxxxxxxx\"", "SSECustomerAlgorithm": "AES256", "SSECustomerKeyMD5": "NX6C25NPxF9KJbS4Pxxxxx==" }
$ aws s3api head-object --bucket $BUCKET --key sse-upload --sse-customer-algorithm AES256 --sse-customer-key $KEY { "AcceptRanges": "bytes", "LastModified": "2021-05-24T02:03:42+00:00", "ContentLength": 11, "ETag": "\"3a2def088089b2d7d7aee19ca9bxxxxx\"", "ContentType": "binary/octet-stream", "Metadata": {}, "SSECustomerAlgorithm": "AES256", "SSECustomerKeyMD5": "NX6C25NPxF9KJbS4Pxxxxx==" }
S3のコンソールからはサーバー側の暗号化設定、メタデータなどは空欄になっている。
コンソールからダウンロードしようとすると以下のように拒否される。
<Error> <Code>InvalidRequest</Code> <Message>The object was stored using a form of Server Side Encryption. The correct parameters must be provided to retrieve the object.</Message> <RequestId>AQVE86HB0HCNQW7Z</RequestId> <HostId>xxxxxxxxxxxxxxx7bXv0ZCp9Ukn4OX710r7UZR/Kl3Dql9cdNr6Tv+rH8EFnHXeqqvejVBK1BthWGo=</HostId> </Error>
refs.
- SSE-CのJavaと.NETの実装例 お客様が用意したキーによるサーバー側の暗号化 (SSE-C) の指定 - Amazon Simple Storage Service
- SSE-CのJavaでの実装例 S3のユーザ提供キーによるサーバサイド暗号化 (SSE-C) を試してみた | DevelopersIO
- Ruby SDKでの実装例 S3のServer-Side Encryption(サーバー側の暗号化)の3種類をRubyでやってみた - Qiita
- Shrineではこんな感じの設定で利用できるらしい AWS S3 · Shrine
CSE (クライアントサイド暗号化)
SDKのModule: Aws::S3::Encryptionを使うとCSEになる。CSEにも以下の2種類がある。
- AWS-KMSの鍵を使う
- 上記SDKのパラメータは
kms_key_id, kms_client
- 上記SDKのパラメータは
- ローカルの鍵を使う
- 上記SDKのパラメータは
encryption_key
- 上記SDKのパラメータは
- 本当はもう一つKeyProviderを渡す形式もあるけど割愛
Pros/Cons
- Pros
- 透過的復号がされないので、クレデンシャル漏洩に効果がある
- Cons
- 自前のサーバーで一旦、復号しないといけないので、エンドユーザーに渡すようなコンテンツの場合、帯域、リソース、メモリを消費する
- Presigned URLが使えないので、エンドユーザーからの直接アップロード、ダウンロードが使えない
- ローカルの鍵の場合、鍵の管理が必要
- credentials, KMSなど利用するといい
CSEで暗号化されたかどうかはS3上のオブジェクトのメタデータで確認できる。 x-amz-meta-x-amz-keyがあればCSE。暗号化に使われたデータキーがここに収納されている。
CSEのJavaと.NETの実装例 クライアント側の暗号化を使用したデータの保護 - Amazon Simple Storage Service