CloudFrontとS3の署名付きURL：アクセス制御の違い

# はじめに

署名付きURLとは、S3に格納しているコンテンツに対して一時的なアクセス権限を付与するためのURLのこと。

構成は以下の２種類ある。
「CloudFront + S3」の構成では、CloudFrontのオリジンにS3を設定し、CloudFront経由でS3からコンテンツを取得できるようしている。

* CloudFront + S3 署名付きURL 
* S3 署名付きURL

今回はアクセス制御に着目し、それぞれ違いを整理する。
※こちらの記事ではリソース作成方法には触れていないため、リンクとして掲載している記事を参考にしていただきたい。

## 使い分けポイント

アクセス制御の話の前に、簡単に使い分けのポイントを説明する。
一言で言うと、CloudFrontの機能（カスタムドメイン、キャッシュなど）を使いたい場合は「CloudFront + S3」の構成が適している。

S3署名付きURLは、以下のようにS3ドメインとなる。
```ƒ
https://<s3バケット名>.s3.<リージョン>.amazonaws.com/<対象ファイルのパス>?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=xxxxxxxxxx
```
一方でCloudFront署名付きURLは、以下のようにカスタムドメインにすることができる。
```
https://<example.comなどカスタムドメイン（カスタムドメインを設定していない場合は<ディストリビューションID>.cloudfront.net)>/<対象ファイルのパス>?Expires=<有効期限>&Signature=xxxxxxxxxx__&Key-Pair-Id=<キーペアID>
```
カスタムドメインをCloudFrontに適用する方法は以下サイトをご参照。

https://qiita.com/kanadeee/items/752e20af5b071ef0e011


例えば、S3にコンテンツを格納していることをユーザーに知られたくない場合や、クライアントからのアクセスをドメイン制限している（S3ドメインはアクセス不可としている）場合は、カスタムドメインを使えるCloudFrontが適している。

また、大量のアクセスが見込まれる場合は、CloudFrontのキャッシュ機能が使える。

  
# アクセス制御の違い

さて、ここからが本題である。
「①署名付きURLを生成する」仕組みや、「②生成されたURLにアクセスし、コンテンツを取得する」ときのアクセス制御の仕組みが異なるため、押さえておきたい。

こちらの記事では簡単な例として、EC2からAWS CLIコマンドにより署名付きURLを生成し、アクセス制御の仕方を検証する。


## S3 署名付きURL

S3署名付きURLの場合、IAMユーザーまたはIAMロールのIAM認証情報を利用する２パターンが存在する。

※前提としてS3署名付きURLのAWSリソースの作り方は以下をご参照。IAMユーザーまたはIAMロールに紐づくIAMポリシーにて、対象のS3へのアクセス権限を付与していることが分かる。

https://qiita.com/tmiki/items/87697d3d3d5330c6fc08#s3-presigned-url%E3%82%92%E8%A9%A6%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B










### IAMユーザーを利用する場合

#### ①署名付きURLを生成


EC2にてIAMユーザーのアクセスキーとシークレットアクセスキーの設定を行う。
```
aws configure
AWS Access Key ID [None]: xxxxxxxxxxxxxxxxxxWZ
AWS Secret Access Key [None]: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxp1
Default region name [None]: ap-northeast-1
Default output format [None]: json
```

「ファイル格納パス」を指定して「aws s3 presign」コマンドを実行する。
ここでオプションに「有効期限」を指定する。
```
aws s3 presign s3://s3-test-20241222/macaroon.jpg --expires-in 604800
```

すると以下のような署名付きURLが生成される。（URLは一部省略している）
```
https://s3-test-20241222.s3.ap-northeast-1.amazonaws.com/macaroon.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=xxxxxxxxxxxxxxxxxxWZ%2F20241225%2Fap-northeast-1%2Fs3%2Faws4_request&X-Amz-Date=20241225T143321Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=host&X-Amz-Signature=xxxxxxxxxxxxxxxxxx59
```

#### ②署名付きURLにアクセス・コンテンツ取得

有効期限内であれば①で生成したURLにブラウザからアクセスすると、コンテンツを取得することができる。

ただしあくまで、EC2に認証情報を設定したIAMユーザーのIAMポリシーに、S3に格納されているコンテンツへのGetObject権限がある場合のみ、コンテンツが取得できる。

![スクリーンショット 2024-12-25 23.45.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3933735/4352658e-1f9a-3c71-8ad7-5439a1c77449.png)

:::note info
IAMユーザーを利用したS3署名付きURLのアクセス・コンテンツ取得は、IAMユーザーのIAMポリシーによりアクセス制御している。
:::

例えば、IAMユーザーのIAMポリシーに、S3に格納されているコンテンツへのGetObject権限が付与されていなければ、以下のように「AccessDenied」エラーとなり、コンテンツを取得できない。
（IAMユーザー「IAM-user-dummy」にはS3に格納されているコンテンツを取得する権限がないというエラーが出ている。）

![image-mns7zxkctf8g7k9nh5qgrp6pna.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3933735/e2890ada-9309-447a-ccfb-50d514009a91.png)

IAMユーザーを利用したS3署名付きURLの「①署名付きURLを生成」「②署名付きURLにアクセス・コンテンツ取得」のアクセス制御を図にまとめてみた。

![image-3ba36z7ocbb738gh7cjpbkyx9w.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3933735/464e641d-ae95-0cc9-69ec-ba540d5f5ebd.png)

![image-nqcjoigmdt8tpm8wsa181ctn1a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3933735/3d8de611-648a-6b4d-a58d-02c9d4581ddd.png)


:::note warn
IAMユーザーを利用した方法では、アクセスキーとシークレットアクセスキーが漏洩すると、どこからでもコンテンツ取得可能なS3署名付きURLを生成できてしまう。
そのため次に紹介するIAMロールを利用する方が良いと考える。
:::










### IAMロールを利用する場合

#### ①署名付きURLを生成


まずは先ほどのIAMユーザーのアクセスキーとシークレットアクセスキーの設定を削除する。
「ファイル格納パス」を指定して「aws s3 presign」コマンドを実行する。
ここでオプションに「有効期限」を指定する。
```
aws s3 presign s3://s3-test-20241222/macaroon.jpg --expires-in 604800
```

すると以下のような署名付きURLが生成される。（URLは一部省略している）
```
https://s3-test-20241222.s3.ap-northeast-1.amazonaws.com/macaroon.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=xxxxxxxxxxxxxxxxxxXX%2F20241225%2Fap-northeast-1%2Fs3%2Faws4_request&X-Amz-Date=20241225T081549Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=host&X-Amz-Security-Token=IQoJbxxxxxxxxxxxxxxxxxxX-Amz-Signature=xxxxxxxxxxxxxxxxxx17
```


#### ②署名付きURLにアクセス・コンテンツ取得

有効期限内であれば①で生成したURLにブラウザからアクセスすると、コンテンツを取得することができる。

ただしあくまで、EC2に紐づくIAMロールのIAMポリシーに、S3に格納されているコンテンツへのGetObject権限がある場合のみ、コンテンツが取得できる。


![スクリーンショット 2024-12-25 19.36.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3933735/e2f32832-1a02-8513-7646-d3e96979d1a0.png)

:::note info
IAMロールを利用したS3署名付きURLのアクセス・コンテンツ取得は、URL発行元となるリソース（EC2やLambdaなど）に紐づくIAMロールのIAMポリシーによりアクセス制御している。
:::



例えば、EC2に紐づくIAMロールのIAMポリシーに、S3に格納されているコンテンツへのGetObject権限が付与されていなければ、以下のように「AccessDenied」エラーとなり、コンテンツを取得できない。
（IAMロール「IAM-role-dummy」にはS3に格納されているコンテンツを取得する権限がないというエラーが出ている。）

![image-jyp8rph93prdupk5b19h9nk91c.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3933735/999ad22a-6b0f-075a-f1b6-f4a669206f23.png)



IAMロールを利用する場合、STSにより付与される一時的な認証情報（アクセスキー、シークレットアクセスキー、セッショントークン）を用いて署名を行っていると考えられる。





IAMロールを利用したS3署名付きURLの「①署名付きURLを生成」「②署名付きURLにアクセス・コンテンツ取得」のアクセス制御を図にまとめてみた。

![image-tfdkg1muwjfh7r7mbfbykiu5pc.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3933735/2233931b-30e1-cf5d-260f-051759ebd244.png)

![image-dy78oab4kpnw8rbpufjnyqf1qw.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3933735/786000c5-2f24-414d-3031-d494432d82a2.png)












## CloudFront + S3 署名付きURL

※前提としてCloudFront + S3 署名付きURLの構成の作り方は以下をご参照。CloudFrontを経由させる場合はCloudFrontのキーグループに公開鍵を登録し、公開鍵認証を行うとうことが理解できる。

https://qiita.com/sugimount-a/items/382e10fbbc85bbca1f3d


### ①署名付きURLを生成

EC2にて「aws cloudfront sign」コマンドを実行する。
ここでオプションに「URL（ファイル格納パス）」「（CloudFrontのキーグループに登録している）公開鍵ID」「秘密鍵」「有効期限」を指定する。

```
aws cloudfront sign \
--url https://d1094ns0f2okr5.cloudfront.net/macaroon.jpg \
--key-pair-id K1xxxxxxxxxxxx \
--private-key file:///tmp/private_key.pem \
--date-less-than "$(date -d '+7 days' -u +%Y-%m-%dT%H:%M:%SZ)"
```

すると以下のような署名付きURLが生成される。（URLは一部省略している）
```
https://d1094ns0f2okr5.cloudfront.net/macaroon.jpg?Expires=1735727225&Signature=xxxxxxxxxxxxKlI7g__&Key-Pair-Id=K1xxxxxxxxxxxx
```

### ②署名付きURLにアクセス・コンテンツ取得

有効期限内であれば①で生成したURLにブラウザからアクセスすると、コンテンツを取得することができる。

ただしあくまで、CloudFrontのキーグループに登録されている公開鍵で秘密鍵の署名が正しく検証された場合にのみ、コンテンツが取得できる。

![スクリーンショット 2024-12-25 19.34.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3933735/259339c7-46df-7678-944d-63294d3fcbee.png)


:::note info
CloudFront署名付きURLにアクセス・コンテンツ取得は、CloudFrontのキーグループに登録している公開鍵での署名の検証によりアクセス制御している。
:::

例えば偽の秘密鍵で署名した場合、CloudFrontの公開鍵での署名の検証に失敗する。

```
・偽の秘密鍵「private_key_dummy.pem」を指定する
aws cloudfront sign --url https://d1094ns0f2okr5.cloudfront.net/macaroon.jpg --key-pair-id K1xxxxxxxxxxxx --private-key file:///tmp/private_key_dummy.pem --date-less-than "$(date -d '+7 days' -u +%Y-%m-%dT%H:%M:%SZ)"

・偽の秘密鍵で署名された以下のURLが生成される
https://d1094ns0f2okr5.cloudfront.net/macaroon.jpg?Expires=1735732113&Signature=Wxr3KAx4K3o6zO6e94PZtDff1AROiIiamxvOwCoA0UpSvPEMYbDaPnUuPCV1g3QcGw~~w8ZN7vuRxxqvzzxR-~hOZwxxcEXHxTFT8r-Oe-IhYapnCTfhc91UxlNa2~pF3Vwqlr6Vwza7NNpP4PkkLRSZI-5cawPitF0QBe4gUFfGQ1xCs0nnWIwXg4sM7xHwED4q1Ndglmu07JfVumi0QhI9B9dJA7dR0El12hAvW9-INMB4kojvW4PfuMxBTkvk9y8gGOub2XmZyApqsW0mXrrDsArhJmR~g-u-W2otv9GVNiwh4pSk945EZSO-BEWi3Jocn9INsP-g0P~7GcdJxQ__&Key-Pair-Id=K1xxxxxxxxxxxx
```

ブラウザからアクセスすると「AccessDenied」エラーとなり、コンテンツを取得できない。

![スクリーンショット 2024-12-25 20.53.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3933735/a8e526f5-13df-4d23-d8a1-ab1f99199468.png)

CloudFront署名付きURLの「①署名付きURLを生成」「②署名付きURLにアクセス・コンテンツ取得」のアクセス制御を図にまとめてみた。

![image-z35iuqa76tdauxhztwf6e5iwdo.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3933735/561f48cf-62bc-0ed9-fc60-7ac7434cdda3.png)

![image-mw6y37nerffzjcjue4o6g343sa.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3933735/1a48cefe-326d-42f6-68ad-66fc4e291c1a.png)


:::note warn
CloudFrontを利用した方法では、秘密鍵が漏洩すると、どこからでもコンテンツ取得可能な署名付きURLを生成できてしまう。
そのためキーペアの定期的なローテーションを検討すべき。
:::



ちなみに...
CloudFront署名付きURLにアクセスした場合、CloudFrontのアクセスログには以下のように公開鍵IDが出力される。
```
&Key-Pair-Id=<公開鍵ID>
```
そのためCloudFrontのログを有効化し、アクセスログの公開鍵IDを確認することで、キーペアがローテーションされたことを把握することができる。



# S3とCloudFront署名付きURLの構成は併用できる

これまでS3とCloudFront署名付きURLのアクセス制御の違いを見てきた。
これらのアクセス制御は独立しているため、図のように同一のS3に対し同時に使用することも可能である。

![image-i75b49g55jfcikes7dmi8xskgw.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3933735/cdc093db-f975-83db-c3e6-3256fada05f4.png)


# まとめ

S3とCloudFront署名付きURLのアクセス制御の違いを整理する。

<!--
空欄
-->
* S3 署名付きURL
  * IAMユーザーを利用する場合は、IAMユーザーの認証情報で署名を行い、署名付きURLを生成する
    * ただし認証情報（アクセスキーやシークレットアクセスキー）が漏洩すると、どこからでもコンテンツ取得可能な署名付きURLが生成されてしまうため、IAMロールを利用する方が良いと考える
  * IAMロールを利用する場合は、STSにより付与される一時的な認証情報（アクセスキー、シークレットアクセスキー、セッショントークン）で署名を行い、署名付きURLを生成する
  * URLにアクセスしたとき、URL生成に使用した認証情報（IAMロール、IAMユーザー）に紐づくIAM ポリシーにより、コンテンツ取得の制限を行う
<!--
空欄
-->
* CloudFront + S3 署名付きURL
  * 秘密鍵で署名を行い、署名付きURLを生成する
  * URLにアクセスしたとき、URL生成に使用した秘密鍵での署名に対しCloudFrontに登録している公開鍵で検証を行い、コンテンツ取得の制限を行う
  * ただし秘密鍵が漏洩すると、どこからでもコンテンツ取得可能な署名付きURLが生成されてしまうため、キーペアの定期的なローテーションを検討すべき

<!--
空欄
-->

* 両方に共通すること
  * 「ファイル格納パス」が誤っているとコンテンツを取得できない
  * 「有効期限」を超えるとコンテンツを取得できない