KMSで暗号化されたS3オブジェクトに対し、署名付きURLを発行してダウンロードする

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

S3バケットへのパブリックアクセスを禁止しつつ、一時的なアップロード・ダウンロードを行いたいという場合、署名付きURL(Pre-Signed URL)の利用がまず候補に上がるかと思います。

署名付きURLを利用すると、予め決めた時間まで有効なURLをAWSが払い出してくれ、そのURLに対してアップロード・ダウンロードを行うことができます。

暗号化されていないオブジェクトであれば、特につまずくこともなくすんなり行くかと思いますが、今回KMSで暗号化されたS3オブジェクトに対して署名付きURLを発行する場合に少しつまずいたので、共有させていただきます。

普通に署名付きURLを発行してみる

Python(boto3)でダウンロードを行う署名付きURLを発行しようとすると、次のようなコードになると思います。

import boto3

s3 = boto3.client('s3')
url = s3.generate_presigned_url(
  ClientMethod='get_object', 
  Params={
    'Bucket': bucketname, 
    'Key': key
  }) 

しかし、KMSで暗号化されたS3オブジェクトに対して上記のように発行してアクセスすると、

 
<Error>
<Code>InvalidArgument</Code>
<Message>
Requests specifying Server Side Encryption with AWS KMS managed keys require AWS Signature Version 4.
</Message>
<ArgumentName>Authorization</ArgumentName>
<ArgumentValue>null</ArgumentValue>
<RequestId>24057D1234567890</RequestId>
<HostId>
jqdIuhcHLQRrv1nqb/YrfPWXmVTpIarsldIdedHB+OGi1z1234567890=
</HostId>
</Error>

と署名バージョン4を要求され、ファイルのダウンロードができません。

解決策

Python側で署名バージョン4を次のようなコードで指定してやる必要があります。

import boto3
from botocore.client import Config

s3 = boto3.client('s3', config=Config(signature_version='s3v4'))
url = s3.generate_presigned_url(
  ClientMethod='get_object', 
  Params={
    'Bucket': bucketname, 
    'Key': key
  }) 

以上で払い出されたURLにアクセスすると、

 
$ curl "https://testbucketname12345.s3.amazonaws.com/test.txt?\
X-Amz-Algorithm=AWS4-HMAC-SHA256&\
X-Amz-Credential=AKIAILSPFRZJADABCDEF%2F20180827%2Fap-northeast-1%2Fs3%2Faws4_request&\
X-Amz-Date=20180827T012248Z&X-Amz-Expires=3600&\
X-Amz-SignedHeaders=host&\
X-Amz-Signature=9db6efcc2ea2594b9f08e4cc9873494c75ec381122245b37dfca082d12345678"
testtxtbody

ちゃんとダウンロードできました。

おわりに

KMSによって暗号化されたオブジェクトが、署名バージョン4でないと取得できないということは、S3のドキュメントに書かれていました。 AWS KMS で管理されたキーによるサーバー側の暗号化 (SSE-KMS) を使用したデータの保護 - Amazon Simple Storage Service

また、解決策はboto3のドキュメントに書かれています。 S3 - Boto 3 Docs 1.7.84 documentation