CloudFront Signed Cookie でストリーミング URL を保護

もうコロナ収束しても出社する気がない streampack 木村です。

こういったご時世を反映してか、ライブ配信案件がかなり増加してきています。

HTTP ベースのストリーミングは基本、`来るものは拒まず`なので、何かで制限をかけてあげないといけないのですが、お手軽案として、CloudFront を利用した `Signed Cookie (署名付き cookie)` の簡単な例をご紹介します。

# 構成
使用するリソースは以下です。

- MediaLive
- MediaStore
- S3
- CloudFront

# 前提

- MediaLive、MediaStore、S3、CloudFront については既に構築済みでストリーミングができる状態にあるところから開始します。
- CloudFront には独自ドメインを設定しています。（タダで取得した `streampack.ga`）

# 手順

それではさっそくまいりましょう。

## CloudFront キーペアの作成

まずは CF のキーペアを作成します。 ローカルで作る方法もありますが、AWS コンソールから発行することもできます。（要 root アカウント）

【作成方法】
https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/private-content-trusted-signers.html

出来上がった `プライベートキー` と `アクセスキーID` は大切に保存しておきましょう。

## カスタムポリシーステートメントの用意

署名付き cookie で利用するポリシーを JSON ファイルとして作成します。

設定するパラメータは必要最低限として、これを policy.json として保存します。

```
{"Statement":[{"Resource":"http*://*.streampack.ga/*","Condition":{"DateLessThan":{"AWS:EpochTime":1621555191}}}]}
```

| Items    |       Values | 
|:-----------------|:------------------|
| Resource            | 対象となるホスト、パスを指定（ワイルドカード可）              |
| AWS:EpochTime           | 有効期限を UNIXTIME で指定         |


### 1. ポリシーステートメントを base64 エンコード

下記コマンドを実行して結果をメモしておきましょう。

```
$ cat ./policy.json | openssl base64 | tr '+=/' '-_~'
```

### 2. ポリシーステートメントを署名して base64 エンコード
下記コマンドを実行して結果をメモしておきましょう。

```
$ cat ./policy.json | openssl sha1 -sign ./[privatekey].pem | openssl base64 | tr '+=/' '-_~'
```

### 3. cookie をセットする

上記を結果を用いてセットする cookie を作成します。
Player が CF にアクセスするときに下記 cookie を渡すことにより、認証をパスすることができます。


| cookie    |       value | 
|:-----------------|:------------------|
| CloudFront-Key-Pair-Id             |              アクセスキーID |
| CloudFront-Policy           | 上記１の結果          |
|CloudFront-Signature|上記２の結果|


## CloudFront 設定

### Origins 設定

今回、マルチオリジンを利用して、Player と HLS 取得先をそれぞれ設定します。

![スクリーンショット 2020-05-22 17.05.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172426/c8858372-e7e9-a0c1-8ae6-929efca98f6b.png)

### Behaviors 設定

HTML５ Player を S3 から （Default）
HLS は MediaStore から （ts と m3u8） 取得するように設定します。

![スクリーンショット 2020-05-22 17.09.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172426/7e5adf7b-a027-5bb5-82bd-10071c7d4933.png)

### Restrict Viewer Access 設定

Behaviors の ts と m3u8 設定で `Restrict Viewer Access` を有効にします。
Default はそのまま無効とします。 これは HTML を読み込ませて cookie を取得される為です。

![スクリーンショット 2020-05-22 17.12.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172426/fa160117-690f-d751-a484-848cf3798d98.png)

# Player の用意

今回 HTML で javascript を使用して cookie をセットし、同時に HTML5 Player も埋め込んでしまいます。

Player 例：

```html
<!DOCTYPE html>
<html>
<head>
  <title>Clappr Player</title>
  <meta charset="UTF-8">
  <!-- Player -->
  <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/clappr/0.2.86/clappr.min.js"></script>
</head>
<body>
  <div id="player"></div>
  <script>
	document.cookie = "CloudFront-Key-Pair-Id=XXXXXXXXXXXXXXXXXXXX; path=/";
	document.cookie = "CloudFront-Policy=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; path=/";
	document.cookie = "CloudFront-Signature=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; path=/";
    var player = new Clappr.Player({
      source: "https://xxx.streampack.ga/xxx/index.m3u8",
      parentId: "#player"
    });
  </script>
</body>

```

`document.cookie` に上記で作成した cookie を指定し、ついでに `path=/` も付与することで、サイト全体がこの cookie の対象となります。

`source` には CloudFront 経由の MediaStore の HLS URL を指定します。

# 視聴

S3 にアップした HTML ファイルに CF 経由でアクセスします。
Behaviors Default には制限をかけていないので、HTML ファイルまでは誰でもアクセスできます。

ブラウザがこの HTML を読み込む際に、cookie を取得して、続く m3u8 と ts のリクエストの際に、この cookie を付与することにより、CF の署名付き cookie が認証されて視聴が可能となります。


![スクリーンショット 2020-05-22 17.33.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172426/0d6bfb5e-9325-614e-0560-51a3c52888fd.png)


ちなみに同じ HTML から document.cookie 部分を削除すると CF に怒られて視聴不可になります。

![スクリーンショット 2020-05-22 17.44.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172426/435676d3-7e46-8b08-3ef0-f9cfa01b7fe1.png)

当たり前だけど URL 直打ちも拒否ります！

```
kimura7:~ kimura$ curl https://xxx.streampack.ga/xxx/index.m3u8
<?xml version="1.0" encoding="UTF-8"?>
<Error><Code>AccessDenied</Code><Message>Access Denied</Message><RequestId>D601B63B9BB1393C</RequestId><HostId>qV0Y6iA+8rZx0Q8TM/pn+r22UyykEKnsdyPUHmkFjRd4v8Y/7BHH3gM6prUe6v8d/zxHjSNjXz0=</HostId></Error>
```

# まとめ

今回は超絶、全部手作業ですが、最小構成の場合は結構簡単に設定することができます。


これで ffmpeg -i -movflags faststart.... な人はサヨウナラです。

# 参考
cookie 生成にあたり、@suhirotakaさんが作成された下記の投稿が大変参考になりました。

https://qiita.com/suhirotaka/items/514a9e246779dc1b9489