業務で、S3の署名付きURLのフォーマットを調べる機会がありました。署名付きURL発行方法について、AWSの公式情報をもとに実際に使ってみたのでその内容を紹介します。
トレーニングが好きです。ジムまで徒歩1 分。
S3 署名付きURLとは
S3の署名付きURLは、Amazon S3(Simple Storage Service)内のファイルに対して一時的なアクセス権限を付与するためのURLです。
署名付きURLを使用することで、パブリックアクセスができないバケット内のファイルに対してセキュアにアクセスすることが可能です。
具体的には、生成したURLには署名が含まれており、そのURLを利用することでアクセスが可能になります。その署名はAWSアカウントの認証情報に基づいて生成されるため、認証情報がない場合は署名付きURLを作成できず、ファイルへのアクセスもできません。そして、これらのURLは指定された期間内にのみ有効です。期限を過ぎればアクセスはできなくなるため、必要な時にのみ発行・使用することができます。(署名付きURLが期限よりも早く切れてしまうことがあります。詳しくは認証情報についてのパートで記載します。)
今回は、以下の署名付きURLを発行しました。
- S3ファイルの参照用URL
- S3へのファイルアップロード用URL
セキュリティ上の利点
S3の署名付きURLには以下のようなセキュリティ上の利点があります。
アクセス制御の強化
署名付きURLを使用することで、ファイルへのアクセスを厳密に制御できます。
アクセス権限も署名の生成時に指定することができるため、必要な操作に対してのみアクセス権限を付与することができます。(つまり、参照用のURLでその他の操作はできません。)また上で書いた通り、署名付きURLには期限が設定されており、有効期間が過ぎると自動的に無効化されます。
データの安全性の確保
署名付きURLは、AWSアカウントの認証情報を使用して生成されます。そのため、署名を検証することで、URLのデータが正当であり、改ざんされていないことが確認できます。認証情報は安全に保管されており、外部のユーザーや攻撃者による不正なアクセスを減らし、改ざんを防ぐことができます。
※ 署名付きURLが漏洩した場合は外部のユーザーが一時的にファイルにアクセスできる状態になりますが、期限が切れるとアクセスできなくなることから影響は小さくとどまる可能性が高くなっています。
プライバシーの保護
署名付きURLを使用することで、プライベートなファイルに対してセキュアなアクセスを実現できます。署名付きURLがなければ、ファイルに直接アクセスすることはできません。この仕組みにより、機密性の高いデータに対して、限られたユーザーのみにアクセスを許可することができます。
実際に使ってみる
準備
S3にパブリックアクセス不可のバケットを作成
新しいバケットを作成し、パブリックアクセス不可の設定にしておきます。
認証情報を設定
前述したように、署名付きURL生成時に認証情報が必要です。
認証には以下のいずれかを用いて行います。
- IAMユーザー
- IAMロール(インスタンスプロファイル)
今回は、IAMユーザーの認証情報を設定しました。
以下の情報があれば良いようです。
AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY
AWS_DEFAULT_REGION
アプリケーション内で署名付きURLを発行する場合は、IAMロールを使うといいでしょう。
※ 署名付きURLの有効期限よりもインスタンスプロファイルの一時認証情報の有効期限が短い場合は、一時認証情報の有効期限切れと同時に署名付きURLも失効してしまうので注意が必要です。
参照URLをCLIで発行してみる
ファイルに直接アクセスできないことを確認
- ファイルをアップロード
URLを発行
presign
コマンドを用いて、参照用の署名付きURLを発行可能です。(公式ドキュメント)
$ aws s3 presign s3://presigned-url-test-muro/test.jpg --expires-in 604800
s3://presigned-url-test-muro/test.jpg
ここで、対象のファイルを指定します。--expires-in 604800
オプションで、有効期限を設定可能です。
単位は秒です。(604800 の場合は1週間の有効期限)
アップロード用のURLが返ってきました。
※ 一部の情報はxxxxxx
でマスクしています
https://presigned-url-test-muro.s3.ap-northeast-1.amazonaws.com/test.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=xxxxxxxxxxxxxxxxxxxx%2F20230710%2Fap-northeast-1%2Fs3%2Faws4_request&X-Amz-Date=20230710T054140Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=host&X-Amz-Signature=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
返されたURLにアクセスすることでブラウザで画像を確認できました。
コンソールからも発行可能
コンソールでも発行時に、有効期限の設定が可能です。
期限を1分にして、URLを作成しブラウザから画像を確認することができました。
1分後にリロードすると、想定通り期限が切れて403 が返ってきました。
本当に意図したファイル以外にはアクセスできないか試してみる
署名付きURLを発行するファイルの隣にhoge.jpg
ファイルをアップロードします。
test.jpg の参照用署名付きURLを発行 -> URLのファイル名を hoge.jpg に変えてブラウザからアクセスを試みました。
結果
以下のURLにアクセスしたとろ、署名がマッチしていないというエラーで、隣のファイルにはアクセスできませんでした。
https://presigned-url-test-muro.s3.ap-northeast-1.amazonaws.com/hoge.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=xxxxxxxxxxxxxxxxxxxx%2F20230712%2Fap-northeast-1%2Fs3%2Faws4_request&X-Amz-Date=20230712T083251Z&X-Amz-Expires=604&X-Amz-SignedHeaders=host&X-Amz-Signature=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
上で書いた、「特定のファイルに対するアクセス許可」が守られていることが確認できましたね。
アップロードURLを発行してみる
公式のサンプルコードを参考にURLを発行します。
環境
Java 17 gradle 8.1.1
依存関係にAWS SDKを追加
dependencies { implementation platform('software.amazon.awssdk:bom:2.15.0') implementation 'software.amazon.awssdk:s3' ... }
URL発行のクラスを追加
public class PresignedUrl { private static final Region REGION = Region.AP_NORTHEAST_1; public static void main(String[] args) { // S3Presignerオブジェクトを生成します。 // ※ S3Presigner : 署名付きURLを生成するために使用されるクラス S3Presigner presigner = S3Presigner.builder() .region(REGION) // リージョンをセット .build(); // オブジェクト生成 String bucketName = "presigned-url-test-muro"; String keyName = "sample.txt"; presignedUrl(presigner, bucketName, keyName); } public static void presignedUrl(S3Presigner presigner, String bucketName, String keyName) { // PutObjectRequestオブジェクトを生成します。(ファイルのアップロードに必要な情報を含むオブジェクト) PutObjectRequest objectRequest = PutObjectRequest.builder() .bucket(bucketName) .key(keyName) .contentType("text/plain") .build(); // オブジェクト生成 // アップロードするファイルに対して署名付きURLを生成するために使用される`PutObjectPresignRequest`オブジェクトを生成します。 PutObjectPresignRequest presignRequest = PutObjectPresignRequest.builder() .signatureDuration(Duration.ofMinutes(10)) // 署名付きURLの有効期限を指定 .putObjectRequest(objectRequest) // PutObjectRequestオブジェクトをセット .build(); // オブジェクト生成 // 署名付きURLを発行します。 PresignedPutObjectRequest presignedRequest = presigner.presignPutObject(presignRequest); String myURL = presignedRequest.url().toString(); System.out.println("Presigned URL to upload a file to: " + myURL); } }
発行
クラスを実行してURLが出力されることが確認できました。
Presigned URL to upload a file to: https://presigned-url-test-muro.s3.ap-northeast-1.amazonaws.com/sample.txt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20230711T021352Z&X-Amz-SignedHeaders=content-type%3Bhost&X-Amz-Expires=600&X-Amz-Credential=xxxxxxxxxxxxxxxxxxxx%2F20230711%2Fap-northeast-1%2Fs3%2Faws4_request&X-Amz-Signature=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
アップロードしてみる
CLI から curl コマンドで、先ほど発行した URL を使ってファイルをアップロードします。
--upload-file sample.txt
で対象のファイルパスを指定します。
curl -X PUT -H "Content-Type: text/plain" --upload-file sample.txt 'https://presigned-url-test-muro.s3.ap-northeast-1.amazonaws.com/sample.txt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20230711T021352Z&X-Amz-SignedHeaders=content-type%3Bhost&X-Amz-Expires=600&X-Amz-Credential=xxxxxxxxxxxxxxxxxxxx%2F20230711%2Fap-northeast-1%2Fs3%2Faws4_request&X-Amz-Signature=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
無事アップロードされたことを確認できました。
$ aws s3 ls s3://presigned-url-test-muro 2023-08-01 05:49:16 61 sample.txt 2023-07-12 08:29:35 534505 hoge.jpg 2023-07-10 05:37:22 534505 test.jpg
感想・まとめ
S3の署名付きURLの発行を行ってみました。特にS3にファイルをアップロードするということについて、都度検証されたユーザーのキーをもとに限定的な時間の中でのみアップロード可能にするというのはセキュリティと使いやすさの両方が保たれる機能だと感じました。初めてS3の署名付きURLを使う方の参考になれば幸いです。