S3 の暗号化を SSE-S3 から SSE-KMS に変えた途端、署名付き URL がエラーを返すようになった。
そんな経験はありませんか?
私はあります。
署名付き URL や SSE-KMS は、単体ではよく使う機能ですが、 組み合わせると、権限設計に一癖あります。
本記事では
- なぜ SSE-KMS にすると署名付き URL がエラーになるのか
- 見落としがちな権限設定の落とし穴(IAM ポリシー・KMS キーポリシー)
- ダウンロード(GET)とアップロード(PUT)で異なる注意点
をまとめます。
前提知識
署名付き URL(Presigned URL)とは
S3 のオブジェクトに対して、一時的なアクセスを許可する URL です。
IAM 認証情報を持たないユーザーでも、この URL を使って S3 オブジェクトのダウンロードやアップロードが可能になります。
主な用途はこんな感じです。
- ユーザーにファイルを一時的にダウンロードさせたい
- ユーザーからファイルをアップロードさせたい(フォーム送信など)
# 署名付き URL の生成例(ダウンロード用・有効期限 3600 秒)
aws s3 presign s3://your-bucket/your-file.pdf --expires-in 3600
気を付けないといけないのは、署名付き URL は生成者の権限を借りてアクセスする ということです。
生成者に権限がなければ、URL を発行できても実際のアクセスは失敗します。
SSE-KMS とは
S3 のサーバーサイド暗号化の一つで、AWS KMS のカスタマーマネージドキーを使ってオブジェクトを暗号化する方式です。
S3 の暗号化方式を比較すると以下の通りです。
| 方式 | 鍵の管理者 | 署名付き URL との相性 |
|---|---|---|
| SSE-S3 | AWS が完全管理 | 〇 問題なし |
| SSE-KMS(AWS マネージドキー) | AWS が管理(aws/s3) |
△ 制約あり |
| SSE-KMS(カスタマーマネージドキー) | ユーザーが管理 | △ 制約あり |
SSE-S3 なら動くのに SSE-KMS だとエラーになる理由
SSE-S3 は S3 が内部で鍵を管理するため、追加の権限設定は不要です。
一方で SSE-KMS は KMS キーへのアクセス権限が別途必要 になります。ここが気を付けないといけない部分です。
注意したいのは、SSE-KMS で AWS マネージドキー(aws/s3)を選んだ場合も例外ではないということです。
「AWS が管理しているキーだから、SSE-S3 と同じように権限不要では?」と思いがちですが、SSE-KMS である以上、キーの管理者に関係なく KMS への権限が必要 です。
署名付き URL でオブジェクトにアクセスするとき、内部では以下が起きています。
SSE-S3 の場合
- 署名付き URL でリクエスト
- S3 が内部の鍵でデータを復号
- データを返す
SSE-KMS の場合
- 署名付き URL でリクエスト
- S3 が KMS にデータキーの復号を依頼 ← ここで KMS キーへの権限がないとエラー
- KMS がデータキーを復号して返す
- S3 がデータキーでデータを復号
- データを返す
SSE-KMS では S3 へのアクセス権限に加えて、KMS キーへの権限も必要 です。
署名付き URL の生成者が KMS キーの権限を持っていなければ、URL は発行できても実際のリクエストでエラーが返ります。
ハマりどころ
1. IAM ポリシーに KMS 権限が必要
署名付き URL を生成する IAM ユーザーやロールのポリシーに、KMS 関連の権限を追加する必要があります。
ダウンロード(GET)の場合
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowS3GetObject",
"Effect": "Allow",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-bucket/*"
},
{
"Sid": "AllowKMSDecrypt",
"Effect": "Allow",
"Action": "kms:Decrypt",
"Resource": "arn:aws:kms:ap-northeast-1:123456789012:key/your-key-id"
}
]
}
s3:GetObject だけでは不十分で、kms:Decrypt がないとエラーが発生します。
アップロード(PUT)の場合
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowS3PutObject",
"Effect": "Allow",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::my-bucket/*"
},
{
"Sid": "AllowKMSForUpload",
"Effect": "Allow",
"Action": "kms:GenerateDataKey",
"Resource": "arn:aws:kms:ap-northeast-1:123456789012:key/your-key-id"
}
]
}
アップロード時は kms:GenerateDataKey が必要です。
S3 がオブジェクトを暗号化するために、KMS にデータキーの生成を依頼するからです。
注意
マルチパートアップロードの場合は kms:Decrypt も必要です。
各パートの暗号化で同じデータキーを再利用するため、暗号化データキーの復号が発生します。
2. Boto3 で Signature Version 4 を明示する
SSE-KMS を使う場合、Signature Version 4(SigV4)が必須 です。
AWS 公式ドキュメントにも明記されています。
最近のバージョンの Boto3 はデフォルトで SigV4 を使いますが、環境や設定によっては SigV2 になるケースがあります。
その場合、以下のような InvalidArgument エラーが返ります。
<Error>
<Code>InvalidArgument</Code>
<Message>
Requests specifying Server Side Encryption with AWS KMS managed keys
require AWS Signature Version 4.
</Message>
<ArgumentName>Authorization</ArgumentName>
</Error>
確実に動かすには、クライアント生成時に明示的に SigV4 を指定します。
from botocore.config import Config
s3_client = boto3.client("s3", config=Config(signature_version="s3v4"))
私も実際にこれでハマりました。
SSE-S3 のときは問題なかったのに、SSE-KMS に変えた途端に署名付き URL が通らなくなり、原因がこの SigV4 の指定漏れでした。
注意
以下のような環境では SigV4 がデフォルトにならないことがあります。
- 古いバージョンの Boto3 / Botocore
-
~/.aws/configでsignature_versionが明示的に設定されている - レガシーなリージョン設定
SSE-KMS を使うなら、常に Config(signature_version="s3v4") を指定する のが安全です。
3. KMS キーポリシーの許可
デフォルト設定で KMS キーを作成した場合は、root の委任ポリシーがあるため IAM ポリシーだけで通ります。
キーポリシーを変更している場合は、KMS キーポリシー側でも許可されていないとエラーが発生します。
KMS キーにはキーポリシーがあり、「誰がこの鍵を使えるか」を制御しています。
IAM ポリシーとキーポリシーの 両方 で許可されている必要があります。
キーポリシーの設定例
{
"Sid": "AllowPresignedUrlRole",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:role/PresignedUrlGeneratorRole"
},
"Action": [
"kms:Decrypt",
"kms:GenerateDataKey"
],
"Resource": "*"
}
補足
KMS キーポリシーの Resource は * で OK です。
キーポリシーはキー自身に紐づくため、* はそのキー自体を指します。
デフォルト設定で作成した KMS キーには、Principal に root が指定されたポリシーが含まれています。
root を指定すると、そのアカウントの IAM ポリシーに判断を委任する意味になるため、この場合は IAM ポリシーでの許可だけで KMS キーを使えます。
ただし、セキュリティ要件でこの記述を削除・変更している場合は、キーポリシーで明示的に署名付き URL 生成者のプリンシパルを許可する必要があります。
4. PUT 時の署名付き URL 生成に SSE 指定が必要
バケットのデフォルト暗号化を使わず、リクエストで明示的に KMS キーを指定する場合の話です。
デフォルト暗号化が SSE-KMS に設定されている場合は、S3 がサーバー側で自動的に暗号化するため、この手順は不要です。
アップロード用の署名付き URL を生成する際、SSE-KMS のパラメータを署名に含める 必要があります。
AWS CLI の場合
AWS CLI の presign コマンドは GET(ダウンロード)用途を想定したコマンドです。
PUT 用途で SSE-KMS パラメータを含めたい場合は、SDK を使う方が確実です。
AWS SDK(Python / Boto3)の場合
import boto3
from botocore.config import Config
s3_client = boto3.client("s3", config=Config(signature_version="s3v4"))
# ダウンロード用(GET)
download_url = s3_client.generate_presigned_url(
"get_object",
Params={
"Bucket": "my-bucket",
"Key": "my-file.pdf",
},
ExpiresIn=3600,
)
# アップロード用(PUT)
upload_url = s3_client.generate_presigned_url(
"put_object",
Params={
"Bucket": "my-bucket",
"Key": "upload/new-file.pdf",
"ServerSideEncryption": "aws:kms",
"SSEKMSKeyId": "arn:aws:kms:ap-northeast-1:123456789012:key/your-key-id",
},
ExpiresIn=3600,
)
PUT 用の署名付き URL では、ServerSideEncryption と SSEKMSKeyId を Params に含めます。
注意
PUT リクエスト時のヘッダーも署名に含めたパラメータと一致させる必要があります。
クライアント側(curl や fetch)でリクエストする際に、以下のヘッダーを付与してください。
curl -X PUT \
-H "x-amz-server-side-encryption: aws:kms" \
-H "x-amz-server-side-encryption-aws-kms-key-id: arn:aws:kms:ap-northeast-1:123456789012:key/your-key-id" \
--upload-file ./new-file.pdf \
"<presigned-url>"
ヘッダーが不足していると SignatureDoesNotMatch エラーになります。
補足
マルチパートアップロード(upload_part)の場合、SSE パラメータは create_multipart_upload の時点で指定します。
各パートの署名付き URL には ServerSideEncryption や SSEKMSKeyId は不要です。
トラブルシューティング
署名付き URL × SSE-KMS でエラーが出た場合のチェックリストです。
| # | 確認項目 | 対象 |
|---|---|---|
| 1 | IAM ポリシーに kms:Decrypt があるか |
署名付き URL 生成者 |
| 2 | IAM ポリシーに kms:GenerateDataKey があるか(PUT の場合) |
署名付き URL 生成者 |
| 3 | Boto3 で Config(signature_version="s3v4") を指定しているか |
アプリケーション |
| 4 | KMS キーポリシーで生成者のプリンシパルが許可されているか | KMS キー |
| 5 | PUT 時に SSE パラメータを署名に含めているか(デフォルト暗号化を使わない場合) | アプリケーション |
| 6 | PUT リクエストのヘッダーが署名と一致しているか(デフォルト暗号化を使わない場合) | クライアント |
| 7 | 署名付き URL の有効期限内か | URL |
| 8 | KMS キーが有効な状態か(無効化・削除スケジュール中でないか) | KMS キー |
まとめ
署名付き URL と SSE-KMS の組み合わせでハマるのは、SSE-KMS にすると KMS キーへの権限が別途必要になる ことが最大の原因です。
加えて、SigV4 の指定や SSE パラメータの扱いにも注意が必要です。
| SSE方式 | 追加で必要な権限設定 |
|---|---|
| SSE-S3 | なし |
| SSE-KMS | IAM ポリシー + KMS キーポリシー |
覚えておきたいことは以下の通りです。
-
SSE-KMS にしたら KMS 権限を追加する —
kms:Decrypt、PUT ならkms:GenerateDataKey -
Boto3 では
Config(signature_version="s3v4")を明示する — SigV4 でないとInvalidArgumentになる - KMS キーポリシーも見る — IAM ポリシーだけでは不十分な場合がある
-
PUT の署名付き URL には SSE パラメータを含める — ヘッダーの不一致は
SignatureDoesNotMatchに
SSE-S3 で動いていたものを SSE-KMS に切り替えるタイミングでエラーに遭遇しがちなので、移行時はこの記事のチェックリストを参考にしてみてください!