[AWS] CloudFront + S3 署名付きCookieでHLSコンテンツのプライベート配信

今、作ってる動画配信プラットフォームの備忘録として書きました。

## 構築中プラットフォームのイメージ

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550573/f851298e-4c3f-5a86-0850-014837dd7db0.png" width="600">

今回は、CloudFront + S3でHLSコンテンツを配信する構成に署名付きCookieを使用しプライベート配信を構築を
[【署名付き Cookie を使用して HLS コンテンツを取得してみた】](https://dev.classmethod.jp/articles/get-hls-using-signedcookie/)こちらを参考に手を動かして実際にやってみた。

##署名付きCookieとは
ユーザーに特定のCookieがセットされていればコンテンツにアクセスできる仕組みです。
[【AWS公式ドキュメント】署名付きCookieの使用](https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/private-content-signed-cookies.html)
※CloudFront側での設定が必要です。

①ユーザーが有料コンテンツに支払いなどアクセス用件を満たすとアプリは`Set-Cookie`ヘッダーをレスポンスで返し、ブラウザ(ビューワー)は名前と値のペアを格納

②ユーザーが有料コンテンツに対しリクエストをするタイミングで①でセットされた名前と値のペアを取得し、リクエストのCookieヘッダーに追加する。これが署名付きCookie

③CloudFrontは署名付きCookieの署名を検証しアクセス許可、拒否を判断する


##署名付きCookieと署名付きURL
上述した署名付きCookieと似たサービスに署名付きURLという仕組みがあります。
結論、今回のHLS形式ような複数のファイルに対してアクセスを提供する場合は署名付きCookieが推奨されています。
[【AWS公式ドキュメント】署名付き URL と署名付き Cookie の選択
](https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/private-content-choosing-signed-urls-cookies.html)

##やってみた
##構成
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550573/971d2f09-5608-8017-e9e0-c5320d2b0055.png" width="600">
###CloudFront キーペア作成
[【AWS公式ドキュメント】信頼された署名者の CloudFront キーペアを作成する
](https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/private-content-trusted-signers.html#private-content-creating-cloudfront-key-pairs)
①rootアカウントでログイン、アカウント名クリックし`マイセキュリティ資格情報`を開く
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550573/9b95e505-6627-a144-f43f-9b2dc3b37f9f.png" width="600">
②`CloudFront のキーペア`を開き新しいキーペアの作成をクリックし、各キーをダウンロード(※取り扱い注意)
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550573/e74a131e-12f8-15c0-16ee-84709c6fcc77.png" width="600">
`アクセスキーID`は後ほど使うのでメモ



###S3にファイルを置く

特に指定なくバケットを作成しHLSコンテンツをアップロード
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550573/24116743-3116-c2eb-a95e-049e1b131653.png" width="600">

###CloudFrontディストリビューション作成
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550573/a5588c6f-8a6f-243e-0659-3a32ef004ed0.png" width="600">

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550573/2cbdda53-462b-315e-34ec-7572cfb8bf1c.png" width="600">

Trusted Signer`Self`にチェックを入れます。署名者は今回作成する、
ディストリビューションと同じアカウントにするので、`Self`とします。
別のアカウントの署名者を使用する場合は、`Specify Accounts`にチェック
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550573/5815bfc8-08a7-c8cd-c474-dda016c31ca0.png" width="600">
各項目設定後ディストリビューションの作成
`Grant Read Permissions on Bucket`をYesにしているとOriginに指定したバケットにポリシーが追加される。

AWSの設定はここまで。

###リクエスト

【S3に直接リクエスト】
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550573/f0f31e68-6ed3-0abd-f282-6a8752bc779c.png" width="600">

【署名付きCookieなしでCloudFrontにアクセス】
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550573/d672d8fa-beb1-591d-81e0-8ac5f5e3aa5f.png" width="600">


それぞれ設定通りに弾いてくれています。

###署名付きCookieを使ってリクエスト
署名付きCookieを使ってリクエストを送信するにはポリシーの作成が必要です。
ポリシーには規定ポリシーとカスタムポリシーの２種類が存在します。

|説明           | 規定ポリシー | カスタムポリシー |
|:-----------|:------------|:------------ |
|ポリシーを複数のオブジェクトへの使用    | ×       | ○         |
|アクセス開始日時の指定   　　　　　　| ×      | ○ (オプション)       |
|アクセスできなくなる日時の指定       | ○        | ○        |
|IPアドレス、IPアドレス範囲指定       | ×          | ○(オプション)          |



**規定ポリシーとカスタムポリシーではセットするCookieが異なるので注意**

[【AWS公式ドキュメント】署名付きCookieの使用
](https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/private-content-signed-cookies.html)

| 規定ポリシー | カスタムポリシー |
|:------------|:------------ |
| **CloudFront-Expires** |**CloudFront-Policy**    |
|CloudFront-Key-Pair-Id  | CloudFront-Key-Pair-Id  |
| CloudFront-Signature | CloudFront-Signature |

今回はHLS形式で複数のファイルに対してリクエストを送るのでカスタムポリシーを使います。

#####ポリシーの作成
policy.jsonを作成
AWS:EpochTimeには必要な終了日時をUnix時間形式で指定

```json:policy.json
{
    "Statement":
    [
        {
            "Resource":"http://xxxxxx.cloudfront.net/*",
            "Condition":
            {
                "DateLessThan":
                {
                    "AWS:EpochTime":1630163409
                }
            }
        }
    ]
}
```

インデント等を取り除きます。

```json:policy.json
{"Statement":[{"Resource":"http://xxxxxx.cloudfront.net/*","Condition":{"DateLessThan":{"AWS:EpochTime":1630163409}}}]}
```

①Base64エンコード

```terminal
$ cat policy.json | openssl base64 | tr '+=/' '-_~'
```
②署名されたBase64エンコード

```terminal
$ cat policy.json | openssl sha1 -sign pk-○○○○○.pem | openssl base64 | tr '+=/' '-_~'
```
※キーペア作成時にダウンロードしたキーを指定

それぞれで表示された文字列と`CloudFront-Key-Pair-Id`Cookieにセットしてアクセス
CloudFront-Policy: ①で作成した文字列
CloudFront-Signature: ②で作成した文字列
CloudFront-Key-Pair-Id: メモしておいた`アクセスキーID`

今回は、[Postman](https://www.postman.com/)を使って取得してみます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550573/56f34fcd-6f3b-00b4-6e8a-45a05a8688ff.png" width="600">

ちゃんと取得出来ました！

気が向いたら次はLambdaとAWS Elemental MediaConvertの記事書きます。

###
[その他参考]
[Protect Private Content using CloudFront Signed Cookies
](https://medium.com/@himanshuarora/protect-private-content-using-cloudfront-signed-cookies-fd9674faec3)