はじめに
最近、S3の暗号化をSSE-Cで行うことがあったので、いろいろ調べてみた。
S3のオブジェクトをSSE-Cで暗号化/復号化しつつアップロード、ダウンロードする一連の流れをAWS SDK for Python(boto3)を用いて試してみた。
S3の暗号化について
暗号化を使用することで転送時と保管時にデータを安全に保護できる
- 転送時:S3との間でデータを送受信するとき
- 保管時:S3データセンター内のディスクに格納されているとき
そして、S3の暗号化にはクライアント側での暗号化とサーバ側の暗号化があるらしい。
クライアント側 (CSE: Client Side Encryption)
S3バケットにアップロードする前にあらかじめクライアント側で暗号化しておいて、S3バケットにアップロードする方法
サーバ側 (SSE: Server Side Encryption)
データを受信するアプリケーションまたはサービスによって、送信先でデータを暗号化する方法
暗号化するためのキーの種類によって以下の3つの方法に分類される
1. SSE-S3
- Amazon S3が暗号化キーの生成・管理・保管を行う
- SSE-S3の利用には料金は発生しない
- 標準のS3バケットのリクエスト料金は発生する
- 事前の準備は不要でオプションで有効化するとすぐ利用できる
- 自分で暗号化に利用するキーを選択したり、変更したりはできない
- AES-256で暗号化される
- S3バケット及びオブジェクトにアクセスできる人であれば、復号化できるため、注意が必要
2. SSE-KMS
- AWS KMSが暗号化キーの生成・管理・保管を行う
- SSE-KMSの利用に料金が発生する
- 標準のS3バケットへのリクエスト料金 + AWS KMSへの暗号化・復号化のリクエスト料金
- AWS KMSの設定が別途必要
- SSE-S3と異なる部分
- 任意のキーを用いて暗号化できる
- キー自体へのアクセス制御によって、暗号化・復号化できるユーザーを制御できる
- 設定によってキーの自動ローテーションができる
- キーの利用状況をAWS CloudTrailで追跡することができる
3. SSE-C (← 今回はこれ)
- ユーザー自身が暗号化キーの生成・管理・保管を行う
- AWSではキーを保管しない
- どのキーでどのオブジェクトが暗号化されたのかという情報は自分自身で管理しなければならない
- 暗号化・復号化に伴う料金は発生しない
- 任意のキーを用いて暗号化することができる
- AES-256暗号化タイプを使用
- HTTPSだけサポートしている
- コンソールからアクセスできない
- CLIの
get
コマンドやput
コマンドのオプションとしてキーを指定する - もしくはSDKを使う
- CLIの
- アクセスキーの流出や不正アクセスに強い
- 暗号化キーが盗まれなければ、コンソールのパスワードが突破されても、S3バケットポリシーの設定が甘くて不正アクセスされても、容易にオブジェクトを復号されることはない
- デフォルト暗号化として設定できない
※ デフォルト暗号化として設定できるのは「1. SSE-S3」と「2. SSE-KMS」のみ
SSE-Cで暗号化/復号化してみる
シナリオ
1. アップロード
サンプル画像と暗号に必要なキー情報を用意してput_object
でS3バケットへアップロードする
2. ダウンロード
復号に必要なキー情報を用意してget_object
でローカルのtmpディレクトリ配下に対象画像をダウンロードする
必要な情報(パラメータ)
-
bucket
- バケット名
-
key
- オブジェクトキー名
-
body
- アップロードするファイル(
bytes
orfile-like object
)
- アップロードするファイル(
-
sse-customer-algorithm
- オブジェクトを暗号化する際に使用するアルゴリズム
-
sse-customer-key
- 使用する暗号化キー
- base64でエンコードしたもの
- S3には保存されない
-
sse-customer-key-md5
- 暗号化キーの128ビットMD5ダイジェスト
- 暗号化キーがエラーなく送信されたことを確認するためのメッセージ整合性チェックのために使用される
-
profile
- 名前付きプロファイル
※ 暗号化キーの作成やbase64でのエンコード等に関しては以下の記事が参考になりました。
事前準備
.env
ファイルに必要なパラメータを用意する
BUCKET_NAME='XXXXXXXXXX'
KEY='sample.jpg'
SSE_CUSTOMER_KEY_BASE64='XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX='
SSE_CUSTOMER_KEY_MD5='XXXXXXXXXXXXXXXXXXXXX=='
PROFILE='XXXX'
1. アップロード
1-1. boto3を使用してアップロードする
import os
import boto3
from dotenv import load_dotenv
# .envファイルの内容を読み込む
dotenv_path = os.path.join(os.path.dirname(__file__), '.env')
load_dotenv(verbose=True, dotenv_path=dotenv_path)
# 環境変数を取得
BUCKET_NAME = os.environ.get("BUCKET_NAME")
KEY = os.environ.get("KEY")
SSE_CUSTOMER_KEY_BASE64 = os.environ.get("SSE_CUSTOMER_KEY_BASE64")
SSE_CUSTOMER_KEY_MD5 = os.environ.get("SSE_CUSTOMER_KEY_MD5")
PROFILE = os.environ.get("PROFILE")
# clientの作成
session = boto3.Session(profile_name=PROFILE)
client = session.client('s3')
# s3バケットへファイルをアップロードする
with open(KEY, 'rb') as image:
response = client.put_object(
Bucket=BUCKET_NAME,
Key=KEY,
Body=image,
SSECustomerAlgorithm='AES256',
SSECustomerKey=SSE_CUSTOMER_KEY_BASE64,
SSECustomerKeyMD5=SSE_CUSTOMER_KEY_MD5
)
print(response)
1-2. 確認してみる
The object was stored using a form of Server Side Encryption.
The correct parameters must be provided to retrieve the object.
---
オブジェクトはSSEの形式で保存されています。
オブジェクトを取得するには、正しいパラメータを提供する必要があります。
2. ダウンロード
2-1. boto3を使用してダウンロードする
import os
import uuid
import boto3
from dotenv import load_dotenv
# .envファイルの内容を読み込む
dotenv_path = os.path.join(os.path.dirname(__file__), '.env')
load_dotenv(verbose=True, dotenv_path=dotenv_path)
# 環境変数を取得
BUCKET_NAME = os.environ.get("BUCKET_NAME")
KEY = os.environ.get("KEY")
SSE_CUSTOMER_KEY_BASE64 = os.environ.get("SSE_CUSTOMER_KEY_BASE64")
SSE_CUSTOMER_KEY_MD5 = os.environ.get("SSE_CUSTOMER_KEY_MD5")
PROFILE = os.environ.get("PROFILE")
# clientの作成
session = boto3.Session(profile_name=PROFILE)
client = session.client('s3')
# s3バケットからオブジェクトを取得する
response = client.get_object(
Bucket=BUCKET_NAME,
Key=KEY,
SSECustomerAlgorithm='AES256',
SSECustomerKey=SSE_CUSTOMER_KEY_BASE64,
SSECustomerKeyMD5=SSE_CUSTOMER_KEY_MD5
)
# レスポンスの中身を読み込む
binary = response['Body'].read()
# 画像を保存
img_path = 'tmp/' + str(uuid.uuid4()) + '.jpg'
with open(img_path, "wb") as f:
f.write(binary)
2-2. 確認
AWS CLIで行う場合
シェルスクリプトに以下を記述して実行します。
1. アップロード
#!/bin/bash
# ./.envファイルを読み込んで変数として参照できるようにする
source ./.env
aws s3api put-object \
--bucket $BUCKET_NAME \
--key $KEY \
--body $KEY \
--sse-customer-algorithm AES256 \
--sse-customer-key $SSE_CUSTOMER_KEY_BASE64 \
--sse-customer-key-md5 $SSE_CUSTOMER_KEY_MD5 \
--profile $PROFILE
2. ダウンロード
#!/bin/bash
# ./.envファイルを読み込んで変数として参照できるようにする
source ./.env
UUID=`uuidgen`
IMG_PATH="tmp/${UUID}.jpg"
aws s3api get-object $IMG_PATH \
--bucket $BUCKET_NAME \
--key $KEY \
--sse-customer-algorithm AES256 \
--sse-customer-key $SSE_CUSTOMER_KEY_BASE64 \
--sse-customer-key-md5 $SSE_CUSTOMER_KEY_MD5 \
--profile $PROFILE
参考