TL;DR
- AWS SDK for Java 2.0に、S3Presignerが追加された
- virtual-hosted styleにデフォルトで対応
- httpsにデフォルトで対応
- 有効期限の設定方法が変更
- 署名付URLの生成には、S3Presignerを使用しましょう
はじめに
この記事は、JavaやScalaでAWSを利用するバックエンド、およびSRE業務に従事する人を対象にしています。
S3の署名付URLとは
S3には、オブジェクトのダウンロードとアップロードのために発行するURLに署名をつけることによって、そのオブジェクトへのアクセスを制限する仕組みがあります。
AWS SDK for Java 2.10.12以前
AWS SDK for Java 2.10.12以前では、AwsS3V4Signerを使用して、Presigned URLを生成していました。
以下、サンプルコードは、scalaで記述しています。
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider
import software.amazon.awssdk.auth.signer.AwsS3V4Signer
import software.amazon.awssdk.auth.signer.params.Aws4PresignerParams
import software.amazon.awssdk.http.{SdkHttpFullRequest, SdkHttpMethod}
val request = SdkHttpFullRequest
.builder()
.encodedPath("bucket/key")
.host("s3.ap-northeast-1.amazonaws.com")
.method(SdkHttpMethod.GET)
.protocol("https")
.build()
val params = Aws4PresignerParams
.builder()
.expirationTime(Instant.now().plusSeconds(300)
.awsCredentials(DefaultCredentialsProvider.create().resolveCredentials())
.signingName("s3")
.signingRegion("ap-northeast-1")
.build()
AwsS3V4Signer.create().presign(request, params).getUri
// 生成される署名付URLの形式
// https://s3.ap-northeast-1.amazonaws.com/bucket/key?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20200625T000000Z&X-Amz-SignedHeaders=host&X-Amz-Expires=300&X-Amz-Credential=XXX&X-Amz-Signature=YYY
AWS SDK for Java 2.10.12以後
AWS SDK for Java 2.10.12から、S3Presignerが追加されました1。引き続きAwsS3V4Signerは使用できますが、S3Presignerを使うことが推奨されています。この理由については後述しますが、一方でS3Presignerの内部実装には、依然、AwsS3V4Signerが使用されています。
import software.amazon.awssdk.regions.Region
import software.amazon.awssdk.services.s3.model.GetObjectRequest
import software.amazon.awssdk.services.s3.presigner.S3Presigner
import software.amazon.awssdk.services.s3.presigner.model.GetObjectPresignRequest
val presigner: S3Presigner = S3Presigner
.builder()
.region("ap-northeast-1")
.build()
val request = GetObjectRequest
.builder()
.bucket("bucket")
.key("key")
.build()
val presignRequest = GetObjectPresignRequest
.builder()
.signatureDuration(Duration.ofSeconds(300))
.getObjectRequest(request)
.build()
presigner.presignGetObject(presignRequest).url.toURI
// 生成される署名付URLの形式
// https://bucket.s3.ap-northeast-1.amazonaws.com/key?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20200625T000000Z&X-Amz-SignedHeaders=host&X-Amz-Expires=300&X-Amz-Credential=XXX&X-Amz-Signature=YYY
何が変わった?
1. virtual-hosted style
生成されるURLを前後で比較すると、バケット名の位置が異なることがわかります。
"https://s3.ap-northeast-1.amazonaws.com/bucket/key" // 前: path style
"https://bucket.s3.ap-northeast-1.amazonaws.com/key" // 後: virtual-hosted style
両者の違いについては、以下を参照ください。
val request = SdkHttpFullRequest
.builder()
.encodedPath("key")
.host("bucket.s3.ap-northeast-1.amazonaws.com")
.method(SdkHttpMethod.GET)
.protocol("https")
.build()
なお、AwsS3V4SignerでもSdkHttpFullRequestを上記のようにすることで、virtual-hosted styleの署名付URLが生成できますが、S3Presignerは実装方法を意識する必要がありません。
2. protocolの指定方法
AwsS3V4Signerでは、.protocol("https")
の部分にある通り、プロトコルの指定ができました。しかし、 S3Presignerにはプロトコルを指定するメソッドがありません。これについては、内部的にhttpsがデフォルトで指定されていることに起因します。使用するシーンは想像しにくいですが、以下のようにendpointを上書きすることで、httpを選択することも可能です。
val presigner: S3Presigner = S3Presigner
.builder()
.region("ap-northeast-1")
.endpointOverride(new URI("http://s3.ap-northeast-1.amazonaws.com"))
.build()
3. 有効期限の指定方法
AwsS3V4Signerでは、有効期限として設定したい未来の日時を指定する方法でした。しかし、S3Presignerでは有効期間をDurationとして指定する方法が採用されています。
// 前
val params = Aws4PresignerParams
.builder()
.expirationTime(Instant.now().plusSeconds(300)
// 後
val presignRequest = GetObjectPresignRequest
.builder()
.signatureDuration(Duration.ofSeconds(300))
まとめ
Amazon S3 path-style 廃止予定 – それから先の話 –
AWSは2020年9月30日以降で作成されるバケットについて、path styleをサポートしないことを発表しています。このため、S3Presignerへの変更は早期に対応しておく必要があると言えます。