はじめに
外部クラウドストレージにアップロードしたファイルを、期限付きでダウンロード可能とする機能。これがAWSのS3だと簡単にできる。だが、いざやってみたらハマったのでここに備忘として残す。
公式の文献
デフォルトでは、すべての Amazon S3 オブジェクトはプライベートであり、オブジェクトの所有者のみがアクセスできます。ただし、オブジェクトの所有者は、署名付き URL を作成することで、他のユーザーとオブジェクトを共有できます。署名済み URL は、セキュリティ認証情報を使用してオブジェクトをダウンロードするアクセス許可を期限付きで付与します。署名付き URL をブラウザに入力するか、プログラムで使用してオブジェクトをダウンロードできます。署名付き URL で使用される認証情報は、URL を生成した AWS ユーザーのものです。
S3でパブリックアクセスなしのプライベートで作成しても、署名付きURLを生成することで、期限をつけて対象オブジェクト(ファイル)だけ公開できると言っている。
S3 コンソールの使用
Amazon S3 コンソールで次の手順に従い、オブジェクトを共有するための署名付き URL を生成できます。コンソールを使用した場合、署名付き URL の最大有効期限は作成時点から 12 時間です
AWS CLI を使用する場合
次の例では、AWS CLI コマンドにより、Amazon S3 バケットのオブジェクトを共有するための署名付き URL を生成します。AWS CLI を使用する場合、署名付き URL の最大有効期限は、作成時点から 7 日間です。
AWS CLIで作ると7日間の期限を設定できるが、マネジメントコンソールからだと12時間期限しか設定できない。であればAWSCLIで作る他ない。
CloudShellを開いて、公式サイトに記載されている以下コマンドを実行する。
aws s3 presign s3://my-bucket/testdir/mydoc.txt --expires-in 604800
しかしアクセスするとエラーとなり、そもそも共有できていない。
そこで公式の文献を改めて読むと以下記載が・・・。
注記
2019 年 3 月 20 日より後に開設されたすべての AWS リージョンでは、リクエストで endpoint-url と AWS リージョンを指定する必要があります。すべての Amazon S3 のリージョンとエンドポイントのリストについては、「AWS 全般のリファレンス」の「リージョンとエンドポイント」を参照してください。aws s3 presign s3://amzn-s3-demo-bucket/mydoc.txt --expires-in 604800 --region af-south-1 --endpoint-url https://s3.af-south-1.amazonaws.com
なんじゃそれ、というわけで、リージョン名を記載して再度以下コマンドを実行。
aws s3 presign s3://my-bucket/testdir/mydoc.txt --expires-in 604800 --region ap-northeast-1 --endpoint-url https://s3.ap-northeast-1.amazonaws.com
実行するとプロンプトが返り、同時に以下のようなURLが生成される。
https://s3.ap-northeast-1.amazonaws.com/my-bucket/testdir/mydoc.txt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=XXXXX%2F20250513%2Fap-northeast-1%2Fs3%2Faws4_request&X-Amz-Date=YYYYMMDDDHHSSZ&X-Amz-Expires=604800&X-Amz-SignedHeaders=host&X-Amz-Signature=XXX
これが署名付きURLとなる。このURLにブラウザなどでアクセスすると、s3://my-bucket/testdir/mydoc.txtの内容を参照することができた。
だがしかし、ここでまた困ったことが。
5~10分で期限が切れて見れなくなる
ようやく署名付きURLでS3にアップロードしたファイルを見れるようになったと思いきや、数分で期限切れに。AWSCLIで署名付きURLを生成したら、7日間まで見れるんじゃないの?という期待はあっさり裏切られた。
そこで調べると以下ブログ記事を発見。
・AWS Identity and Access Management (IAM) インスタンスプロファイル: 最大 6 時間有効
・AWS Security Token Service (STS): 最大 36 時間有効 (AWS アカウントユーザーや IAM ユーザーの認証情報など、永続的認証情報を使用して署名した場合)
・IAM ユーザー: 最大 7 日間有効 (AWS 署名バージョン 4 を使用した場合)
という記述があることから最大 7 日間なのかーという部分しか確認せずにExpiresIn=604800を指定してみましたが、残念ながら有効期限より前に失効する状況は改善しませんでした。
それもそのはずで、よく読めば利用している認証情報で有効期限の最大値が決まり、その最大値で有効期限が失効するということになります。
AWS LambdaだとIAM role を利用しているので、AssumeRole アクションにてAWS STS から一時トークンを取得し、その一時トークンで認証を行なっていることになります。
そのため、上記ドキュメントに記載のSTS 利用時に合致し指定している7日を待たずに失効する形になっていました。
なんとまあ難しい制約で、ブログ主の方のようにLambdaを使った場合や、私のようにCloudShellを使って署名付きURLを生成した場合、AWS Security Token Service (STS)の認証を使ったと判断され、7日間分である604800秒を指定しても、その指定時間は無視されてしまう。STSは最大 36 時間有効のURLを生成できるが、デフォルト値がおそらく5-10分でそれが適用されてしまう、というからくり。
IAM ユーザー: 最大 7 日間有効 (AWS 署名バージョン 4 を使用した場合)
上記の記載の通り、IAMユーザーかつAWS署名バージョン4.で署名付きURLを生成する必要がある。
というわけで対応
自分のクライアント、端末にAWSCLIをインストールする。
IAMで利用するIAMユーザーの作成(コンソールログインなし)
署名付きURL生成専用ユーザーを作る。自分の普段使っているユーザーでもよいが、強めの権限がついていると、漏れた時に特権を取られるので、署名付きURL専用ユーザーを別途作る。
S3読取IAMポリシー作成
先に作成した署名付きURL専用ユーザーの権限を作成する。ポリシーの作成をクリック。
リソースをアップロードしたS3バケットのみにGetとListが操作できる権限を指定して、バケット配下をアスタリスクですべて許可しているが、ディレクトリもさらに限定して許可範囲を絞りたい場合、さらにResource部分のS3のARNを追記修正する。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:Get*",
"s3:List*"
],
"Resource": [
"arn:aws:s3:::my-bucket",
"arn:aws:s3:::my-bucket/*"
]
}
]
}
IAMで利用するIAMユーザーにポリシー割り当て
署名付きURL生成専用ユーザーに、S3読取IAMポリシーを割り当てる。
当該IAMユーザーのIAM設定画面で、以下のようにIAMポリシーを追加する。
IAMで利用するIAMユーザーにアクセスキーを生成
署名付きURL生成専用ユーザーにアクセスキーとシークレットキーを生成する。当該IAMユーザーのIAM設定画面で、以下のように生成。
セキュリティ認証情報をクリック。
アクセスキーとシークレットキーを生成したら、絶対に漏れない場所にアクセスキーとシークレットをメモしておく。漏れたら再生成する。
クライアントのAWSCLIに認証情報(変数)を指定
注意)わたしの環境はWindowsクライアントのコマンドプロンプトを使っています。
$ aws configure
AWS Access Key ID [None]: AWS_ACCESS_KEY_ID
AWS Secret Access Key [None]: AWS_SECRET_ACCESS_KEY
Default region name [None]: AWS_DEFAULT_REGION
Default output format [None]:
Default output formatは自由。
アクセスキーとシークレットを変数に指定したら、次に移る。
クライアントのAWSCLIにて、現在のセッションのみにアクセスキーを設定
お使いのクライアントのOS環境に応じて、コマンドは変更。ここでIAMで生成したアクセスキーとシークレットを「=」以降に入力する。
set AWS_ACCESS_KEY_ID=AKIXXXXXXXXX
set AWS_SECRET_ACCESS_KEY=ZZZZZZZZZZZZZ
set AWS_DEFAULT_REGION=ap-northeast-1
ap-northeast-1は自身のリージョンに変更。
AWS CLI のS3 署名にsigv4 を利用するように設定
現在はデフォルトでS3署名をsigv4になっているそうだが、念のために実施しておく。
Default プロファイルの場合、以下。
aws configure set default.s3.signature_version s3v4
個別のプロファイルの場合、以下。
aws configure set profile.your_profile_name.s3.signature_version s3v4
S3確認
認証情報の設定ができたため、対象S3にアクセスができるかを確認する。
aws s3 ls s3://my-bucket/testdir/
2025-05-08 20:51:49 0
2025-05-08 21:17:23 204995 mydoc.txt
署名付きURL(7日間期限)発行
署名付きURL生成専用ユーザーで認証情報が通ったため、署名付きURLを発行する。
604800は7日間の秒換算となる。
aws s3 presign s3://my-bucket/testdir/mydoc.txt --expires-in 604800 --region ap-northeast-1 --endpoint-url https://s3.ap-northeast-1.amazonaws.com
実行するとプロンプトが返り、同時に以下のようなURLが生成される。
https://s3.ap-northeast-1.amazonaws.com/my-bucket/testdir/mydoc.txt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIXXXX%2F20250513%2Fap-northeast-1%2Fs3%2Faws4_request&X-Amz-Date=YYYYMMDDDHHSSZ&X-Amz-Expires=604800&X-Amz-SignedHeaders=host&X-Amz-Signature=XXX
これが署名付きURLとなる。t?X-Amz-Algorithm=AWS4-HMAC-SHA256&
がS3 署名にsigv4 を利用している意味となる。
X-Amz-Credential=AKIXXXX
にアクセスキーが入る。これで7日間期限の署名付きURLを生成することができた。
注意
署名付きURL生成専用ユーザーでアクセスキーとシークレットキーを生成したが、アクセスキー生成は漏洩して特権を奪われた事件などがあり、AWSが非推奨としている。非推奨にしているからといって、署名付きURL生成してすぐに(7日間期限以内に)アクセスキーを消すと、署名付きURLが見れなくなる。このため、7日間期限が終わってからアクセスキーを消すようにする。仮に漏洩してもIAMポリシーでS3の参照権限だけにしているため、大きな問題はないが・・・。
まとめ
「AWSだと簡単にできますよ!」というのは、驕り。世の中そんな簡単じゃない。