げっとシステムログ

WEB開発メモ

golang で aws cloudfront signed cookie

AWS CloudFront でプライベートコンテンツを配信したい。 このために Signed Cookie を使う方法があるが、これを golang を使用して生成したい。

CONTENTS
  1. AWS CloudFront Signed Cookie
  2. カスタムポリシーを使用した Signed Cookie
  3. カスタムポリシーを作成する
  4. カスタムポリシーを base64 エンコードする
  5. キーペアを使用して署名
  6. golang で署名
  7. まとめ
  8. 参考資料
SOURCES
ENVIRONMENTS
  • golang : 1.14.4

AWS CloudFront Signed Cookie

CloudFront でプライベートコンテンツを配信するのはこの記事この記事を参考にして特につまづくことなくできた。 この Signed Cookie を生成するパッケージを作るのに苦労したのでまとめておく。

プライベートコンテンツを配信する方法は Signed URL と Signed Cookie の2つの方法が用意されている。 このうち Signed Cookie を選択した。

理由は Cookie を使用する方法なら HttpOnly にすることで、クライアントがトークンを扱うことなくアクセスできるから。

TOP

カスタムポリシーを使用した Signed Cookie

Signed Cookie には、既定のポリシーを使用するものと、カスタムポリシーを使用するものの2つが用意されている。 このうち、カスタムポリシーを使用するほうを選択した。

理由は、既定のポリシーを使用するほうは単一のファイルのみの対応のため。 別なファイルにアクセスするには Cookie を再発行しなければならない。

これだと Cookie を受け取ってからファイルにアクセスするまでに、別なタブとかで新しい Cookie を受け取ってしまうとアクセスに失敗する。 これではうまくいかない。

複数のリソースを取得できるようにするにはカスタムポリシーを使用する必要がある。

TOP

カスタムポリシーを作成する

まず、カスタムポリシーを作成する。

{
   "Statement": [
      {
         "Resource":"URL of the file",
         "Condition":{
            "DateLessThan":{"AWS:EpochTime":"required ending date and time in Unix time format and UTC"},
            "DateGreaterThan":{"AWS:EpochTime":"optional beginning date and time in Unix time format and UTC"},
            "IpAddress":{"AWS:SourceIp":"optional IP address"}
         }
      }
   ]
}
  • Resource : ファイルの URL。ワイルドカードを指定可能
  • DateLessThan : ここで指定した Unix time までの間、リソースにアクセスできる
  • DateGreaterThan : ここで指定した Unix time から、リソースにアクセスできる
  • IpAddress : ここで指定した IP アドレスからのみ、リソースにアクセスできる

以下がカスタムポリシーの例。

{"Statement":[{"Resource":"https://example.getto.systems/*","Condition":{"DateLessThan":{"AWS:EpochTime":1591811666}}}]}

空白や改行の削除は必須なので注意。

TOP

カスタムポリシーを base64 エンコードする

さらにこいつを base64 でエンコードする。 そして、以下の文字の置き換えを行わなければならない。

  • +=
  • =_
  • /~

base64 の url エンコードとは微妙に異なるので注意。 ただ、デコードはできるのでこういう仕様もあるのだろう。

TOP

キーペアを使用して署名

base64 でエンコードした文字列を手に入れたら、あとは署名するだけ。

署名にはキーペアのプライベートキーを使用する。 この記事を参考にしてダウンロードしておく。

署名までできたら、Cookie に以下の値を設定する。

  • CloudFront-Policy : base64 でエンコードしたポリシー
  • CloudFront-Signature : ポリシーを署名した結果
  • CloudFront-Key-Pair-Id : 使用したキーベアの ID

これでプライベートコンテンツにアクセスできる。

TOP

golang で署名

golang でプライベートキーを扱う方法はこの gist を参考にした。

署名するコードは以下の通り。

import (
    "crypto"
    "crypto/rand"
    "crypto/rsa"
    "crypto/sha1"
    "crypto/x509"
    "encoding/pem"
    "io/ioutil"
    "log"
)

privateKey := ioutil.ReadFile("path/to/key_pair/pk.pem")
policy := byte[]("base64-encoded-policy")

block, _ := pem.Decode(privateKey)
key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
    log.Fatal(err)
}

rng := rand.Reader

hashed := sha1.Sum(policy)
signed, err := rsa.SignPKCS1v15(rng, key, crypto.SHA1, hashed[:])
if err != nil {
    log.Fatal(err)
}

// signed : signed cookie

作ったパッケージは GitHub に置いてある。

TOP

まとめ

ポリシーの作成方法や base64 エンコードの癖があったために難航したけれど、何とか signed cookie でアクセスできるところまでたどり着けた。

TOP

参考資料

TOP