署名付き Cookie を使用して HLS コンテンツを取得してみた

2020.05.20

こんにちは、大前です。

 

CloudFront + S3 で HLS コンテンツを配信する構成で、署名付き Cookie を使用する機会がありましたので備忘録としてブログを書いていきます。

前提

署名付き Cookie とは

署名付き Cookie とは、CloudFront でプライベートなコンテンツ配信を実現する機能の1つです。

署名付き Cookie の使用

 

この機能を使用すると、特定の Cookie がセットされている場合に限り、CloudFront からコンテンツを取得する事が出来る様になります。

※CloudFront 側で署名付き Cookie を利用したリクエスト以外を拒否する設定にする必要があります

 

特定のユーザにのみコンテンツを配信したい時などに、認証の仕組みと組み合わせる事でプライベートなコンテンツ配信が可能となります。

 

なぜ HLS に署名付き Cookie を使うのか

署名付き Cookie と類似する機能として、署名付き URL というものがあります。

署名付き URL の使用

CloudFront+S3で署名付きURLでプライベートコンテンツを配信する

 

署名付き URL も署名付き Cookie と同様にプライベートなコンテンツ配信を実現する機能ですが、HLS(Http Live Streaming) コンテンツを配信する場合には署名付き Cookie を使用する必要があります。

理由としては、HLS は仕組み上複数のファイルに対してリクエストが発生するのですが、署名付き URL ではこれに対応出来ない為です。

HLS の大まかな仕組みとしては以下ブログに図を載せていたりするので、よければご参照ください。

【小ネタ】CloudFront のファイル圧縮機能で HLS コンテンツが圧縮されるのか調べてみた

 

また、AWS 公式ドキュメントでも HLS の様な複数のファイルに対して制限をかけたい場合には署名付き Cookie の使用が推奨されています。

署名付き URL と署名付き Cookie の選択

次のような場合は、署名付き Cookie を使用します。

  • 複数の制限されたファイル (HLS 形式の動画のすべてのファイルやウェブサイトの購読者の領域にあるすべてのファイルなど) へのアクセスを提供する場合。

やってみた

構成

今回の構成は以下です。

署名付き Cookie を有効にした CloudFront + S3 の構成で S3 に HLS コンテンツを格納し、署名付き Cookie を利用して HLS コンテンツを取得してみます。

構築手順

基本的には先人達のブログと共通手順が多い為、細かい部分は適宜省略します。

CloudFront + S3 で署名付き Cookie でプライベートコンテンツを配信する

CloudFront+S3で署名付きURLでプライベートコンテンツを配信する

【SAP対策】CloudFrontで署名付きURLを発行する【やってみた】

 

1. CloudFront キーペアの作成

署名付き Cookie を利用する際には CloudFront キーペアが必要であるため、まずはこれを作成します。

信頼された署名者の CloudFront キーペアを作成する

 

また、CloudFront キーペアの作成にはルートアカウント(IAM ユーザではない、AWS アカウント作成時に払い出されるアカウント)を使用する必要があります。

 

ルートアカウントでログインし、画面右上のアカウント名をクリックして マイセキュリティ資格情報 を開きます。

 

開いたページから CloudFront のキーペア を開き、新しいキーペアの作成 から画面の指示にしたがってキーペアを作成し、ダウンロードします。

 

CloudFront キーペア の作成後に表示される アクセスキーID は後ほど使用するので、メモっておきます。

 

2. S3 バケット作成

次に、S3 バケットを作成します。

特に設定は不要で、バケットを作成します。

 

HLS コンテンツを格納しておきます。

 

(参考)CORS の設定

今回はコマンドからリクエストを実施するので、CORS(Cross-Origin Resource Sharing)の設定はしていません。

ただ、一般に HLS の再生は video.js 等の javascript ライブラリを使用して取得する事がほとんどかと思います。

その場合には、CORS に関する設定を S3、CloudFront に行う必要があるので注意しましょう。

Cross-Origin Resource Sharing (CORS)

https://aws.amazon.com/jp/premiumsupport/knowledge-center/no-access-control-allow-origin-error/

 

3. CloudFront ディストリビューション作成

最後に、CloudFront ディストリビューションを作成します。

 

  • Origin Domain Name ... 上で作成した S3 バケット
  • Restrict Bucket Access ... Yes
  • Origin Access Identity ... Create a New Identity
    • OAI で用いる Identity を新規作成します。
  • Comment ... 任意の文字列
    • 作成する Identity の名前になります。
  • Grant Read Permissions on Bucket ... Yes, Update Bucket Policy
    • Yes にすると、S3 のバケットポリシーが CloudFront のディストリビューション作成時に合わせて更新されます。
    • 自動で更新させたくない場合は No にして手動で S3 のバケットポリシーを更新しましょう。

 

  • Restrict Viewer Access (Use Signed URLs or Signed Cookies) ... Yes
    • これを Yes にすると、署名付き Cookie、もしくは署名付き URL 以外のアクセスを拒否する様になります。
  • Trusted Signer ... Self

 

各項目を設定したら、ディストリビューションの作成を行います。

Grant Read Permissions on Bucket を Yes にしていると、以下の様なバケットポリシーがオリジンとして指定した S3 に追加されていると思います。

OAI を使用すると、特別なユーザが CloudFront ディストリビューションに紐付けられ、S3 のバケットポリシーでそのユーザからのみ Get を許可する様な形になります。

 

AWS 側の構築は以上になります。

署名付き Cookie でリクエストしてみる

S3 に直接リクエスト

まずはブラウザから直接 S3 上のファイルにアクセスしてみます。

作成した CloudFront ディストリビューション以外からのみアクセスを許可しているので、ちゃんと弾かれます。

 

署名付き Cookie なしで CloudFront にアクセス

次に、署名付き Cookie なしで CloudFront に対してアクセスしてみます。

Restrict Viewer Access を Yes にしているので、設定通り弾かれました。

必要な Cookie を全く指定しない状態でリクエストを行うと、エラーコードとしてはMissingKey が表示される様です。

 

署名付き Cookie ありで CloudFront にアクセス

最後に、署名付き Cookie を使って HLS コンテンツを取得してみます。

 

ポリシーの作成

署名付き Cookie を使ってリクエストを行うためには、まずコンテンツが取得出来る条件などを記載したポリシーを作成する必要があります。

このポリシーには 既定ポリシーカスタムポリシー の2種類が存在し、複数のオブジェクトを取得したい場合や、IP アドレスで制限をかけたい場合などはカスタムポリシーを使用する必要があります。

署名付き Cookie の既定ポリシーとカスタムポリシーの選択

CloudFront の署名付き Cookie によるプライベートコンテンツ配信でワイルドカードを使用する

 

既定ポリシーとカスタムポリシーでは、セットする Cookie が少し異なるので注意が必要です。

  • 既定ポリシー
    • CloudFront-Expires
    • CloudFront-Key-Pair-Id
    • CloudFront-Signature
  • カスタムポリシー
    • CloudFront-Policy
    • CloudFront-Key-Pair-Id
    • CloudFront-Signature

既定ポリシーを使用した署名付き Cookie の設定

カスタムポリシーを使用した署名付き Cookie の設定

 

HLS は複数のファイルに対してリクエストする必要がある為、カスタムポリシーを使用する必要があります。

今回は、「sample_720.m3u8」と「sample_720_00001.ts」が取得出来る様にしたいので、以下の様なポリシーを作成しました。

カスタムポリシーでは、ワイルドカードを使用して複数の Resource を指定する事が出来ます。

カスタムポリシーを使用する署名付き Cookie のポリシーステートメントの作成

policy.json

{
    "Statement":
    [
        {
            "Resource":"http://xxxxxx.cloudfront.net/sample_*",
            "Condition":
            {
                "DateLessThan":
                {
                    "AWS:EpochTime":1590937200
                }
            }
        }
    ]
}

 

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

policy.json

{"Statement":[{"Resource":"http://xxxxxx.cloudfront.net/sample_*","Condition":{"DateLessThan":{"AWS:EpochTime":1590937200}}}]}

 

カスタムポリシー使用時は、作成したポリシーから2つの文字列を作成する必要があります。

詳しくはこちらを参照ください。

1. Base64 エンコード
$ cat policy.json | openssl base64 | tr '+=/' '-_~'
eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiaHR0cDovL2R1NHV3NXQwdGR6enIu
Y2xvdWRmcm9udC5uZXQvc2FtcGxlXyoiLCJDb25kaXRpb24iOnsiRGF0ZUxlc3NU
aGFuIjp7IkFXUzpFcG9jaFRpbWUiOjxxxxxxxxxxxxxx

 

2. 署名された Base64 エンコード

pk-xxxxxx.pem には CloudFront キーペア作成時にダウンロードしたキーを指定してください。

$ cat policy.json | openssl sha1 -sign pk-xxxxxx.pem | openssl base64 | tr '+=/' '-_~'
fqrlU-piOoIkErT6m-BnrulDpkjEsNdJjJ8v~nCi-75rXajOQPVgGvEwh50EghHV
Iybl~FTEzYraKpuLpuPhRrlZ7J4NlV4QIS09dFZWoV48LjhoS061hM7q-Lhd3gb0
jjGKf6oVtMd2Akjapc-cvzISv-waKh79p4HK2qs6JJEX4QzUPyBUbaMOXTdyvyOU
XVWXnTyYwgfOMKYPEvJT3UOu1WMVJi5~sgx6upWlBBzD5GqXbwNhc11Vc0aIw-fg
xxxxxxxxxx

 

必要な文字列が出来たので、署名付き Cookie を使用したアクセスをしてみます。

セットする Cookie の一覧と内容は以下です。

  • CloudFront-Policy ... 上記「1. Base64 エンコード」で作成した文字列
  • CloudFront-Key-Pair-Id ... CloudFront キーペア作成時に表示されていたキーID
  • CloudFront-Signature ... 上記「2. 署名された Base64 エンコード」で作成した文字列

 

上記内容を Cookie にセットし、コマンドから「sample_720.m3u8」を取得してみます。

$curl -H 'Cookie:CloudFront-Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNxxxxx;CloudFront-Signature=fqrlU-piOoIkErT6m-BnrulDpkjEsxxxxx; CloudFront-Key-Pair-Id=APKXXXXXXXXXX' http://xxxxxxx.cloudfront.net/sample_720.m3u8

 

取得出来ました!

$curl -H 'Cookie:CloudFront-Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNxxxxx;CloudFront-Signature=fqrlU-piOoIkErT6m-BnrulDpkjEsxxxxx; CloudFront-Key-Pair-Id=APKXXXXXXXXXX' http://xxxxxxx.cloudfront.net/sample_720.m3u8
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:12
#EXT-X-MEDIA-SEQUENCE:1
#EXT-X-PLAYLIST-TYPE:VOD
#EXTINF:8,
sample_720_00001.ts
#EXT-X-ENDLIST

 

「sample_720_00001.ts」も問題なく取得できました。

$curl -H 'Cookie:CloudFront-Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNxxxxx;CloudFront-Signature=fqrlU-piOoIkErT6m-BnrulDpkjEsxxxxx; CloudFront-Key-Pair-Id=APKXXXXXXXXXX' http://xxxxxxx.cloudfront.net/sample_720_00001.ts
(略)

 

問題なく、署名付き Cookie を使用して HLS コンテンツを取得する事が出来ました。

参考までに、各 Cookie が間違っていたり、指定しない場合のエラーコードを載せてみます。

「そもそも署名付き Cookie を使用する為の設定が不足しているのか」「ポリシーで弾かれているのか」など、問題の切り分け等について参考になれば幸いです。

CloudFront-Policy 無し

MissingExpires が返却されます。

$ curl -H 'Cookie:CloudFront-Signature=fqrlU-piOoIkErT6m-BnrulDpkjEsxxxxx; CloudFront-Key-Pair-Id=APKXXXXXXXXXX' http://xxxxxxx.cloudfront.net/sample_720.m3u8
<?xml version="1.0" encoding="UTF-8"?><Error><Code>MissingExpires</Code><Message>Missing Expires query parameter or cookie value</Message></Error>

CloudFront-Policy 間違い

AccessDenied が返却されます。

ポリシーの条件に一致しない場合(有効期限が切れている場合)なども AccessDenied となります。

$ curl -H 'Cookie:CloudFront-Policy=hogehogehugahuga; CloudFront-Signature=fqrlU-piOoIkErT6m-BnrulDpkjEsxxxxx; CloudFront-Key-Pair-Id=APKXXXXXXXXXX' http://xxxxxxx.cloudfront.net/sample_720.m3u8
<?xml version="1.0" encoding="UTF-8"?><Error><Code>AccessDenied</Code><Message>Access denied</Message></Error>

CloudFront-Signature 無し

MissingSignature が返却されます。

$ curl -H 'Cookie:CloudFront-Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNxxxxx; CloudFront-Key-Pair-Id=APKXXXXXXXXXX' http://xxxxxxx.cloudfront.net/sample_720.m3u8
<?xml version="1.0" encoding="UTF-8"?><Error><Code>MissingSignature</Code><Message>Missing Signature query parameter or cookie value</Message></Error>

CloudFront-Signature 間違い

AccessDenied が返却されます。

$ curl -H 'Cookie:CloudFront-Policy=hogehogehugahuga; CloudFront-Signature=hogehogehugahuga; CloudFront-Key-Pair-Id=APKXXXXXXXXXX' http://xxxxxxx.cloudfront.net/sample_720.m3u8
<?xml version="1.0" encoding="UTF-8"?><Error><Code>AccessDenied</Code><Message>Access denied</Message></Error>

CloudFront-Key-Pair-Id 無し

MissingKey が返却されます。

$ curl -H 'Cookie:CloudFront-Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNxxxxx; CloudFront-Signature=fqrlU-piOoIkErT6m-BnrulDpkjEsxxxxx' http://xxxxxxx.cloudfront.net/sample_720.m3u8
<?xml version="1.0" encoding="UTF-8"?><Error><Code>MissingKey</Code><Message>Missing Key-Pair-Id query parameter or cookie value</Message></Error>

CloudFront-Key-Pair-Id 間違い

MissingKey が返却されます。

$ curl -H 'Cookie:CloudFront-Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNxxxxx; CloudFront-Signature=fqrlU-piOoIkErT6m-BnrulDpkjEsxxxxx; CloudFront-Key-Pair-Id=hogehogehugahuga' http://xxxxxxx.cloudfront.net/sample_720.m3u8
<?xml version="1.0" encoding="UTF-8"?><Error><Code>InvalidKey</Code><Message>Unknown Key</Message></Error>

おわりに

署名付き Cookie を使用して HLS コンテンツを取得してみました。

コンテンツの保護などが要件に含まれている場合には必ず出てくる機能だと思いますので、使い方はしっかり抑えておきたいですね。

 

以上、AWS 事業本部の大前でした。