署名付きCookieの使い方

2024/03/08に公開されました。
2024/03/08に更新されました。

署名付きCookieの使い方をまとめました


author: kuribo-

はじめに

インフラチームのkuribo-です。

今回は署名付きCookieについて使い方を調べたのでまとめました。

署名付きクッキーとは

署名付きCookieとはCloud CDNにある機能です。
Cloud LoadbalancingのBackend ServiceやBackend Bucketをインターネット上に公開せずに、
認証された状態のユーザのみがアクセスできるようにするものです。
Googleのドキュメントは以下です。
https://cloud.google.com/cdn/docs/using-signed-cookies?hl=ja

署名付きURLという機能もありますが、仕組みとしては似たようなものなので今回は割愛します。

署名付きCookieのユースケースとしては、ユーザ認証された人に対して署名付きCookieを発行し、
そのCookieを利用してアクセス制限されているBackendに対してアクセスできるようにするものです。
例えば、Cloud Storageに動画ファイル(HLS含む)を配置し、ユーザ認証した人(会員)のみに動画ファイルの配信をするというような感じです。

以下で使い方を説明していきます。

利用するサービス

今回は以下のサービスを利用します。

  • Cloud Loadbalancing
  • Cloud CDN
  • Cloud Storage
    ※今回はCookieの発行にCloud FunctionsやCloud Runを利用しませんが、Webサービスとして使う場合は利用することになります。

リソースの作成

以下の順番でリソースの作成します。

Cloud Storageの作成

以下のコマンドでバケットとindex.htmlを作成します。
BASE_NAMEやPROJECT_IDは適宜修正してください。

export BASE_NAME=signed-cookie-test
export BUCKET_NAME=$BASE_NAME-bucket
export DEFAULT_STORAGE_CLASS=STANDARD
export LOCATION=asia-northeast1
export PROJECT_ID=hogehoge-project
export PROJECT_NUM=`gcloud projects describe $PROJECT_ID --format="value(projectNumber)"`
gcloud config set project $PROJECT_ID

gcloud storage buckets create gs://$BUCKET_NAME \
    --default-storage-class=$DEFAULT_STORAGE_CLASS \
    --uniform-bucket-level-access \
    --location=$LOCATION \
    --project=$PROJECT_ID

gsutil iam ch \
  serviceAccount:service-$PROJECT_NUM@cloud-cdn-fill.iam.gserviceaccount.com:objectViewer \
  gs://$BUCKET_NAME

echo "signed-cookie-test" > index.html

gcloud storage cp ./index.html gs://$BUCKET_NAME

Cloud Loadbalancingの作成

以下のコマンドでCloud Loadbalancingの作成します。

export BASE_NAME=signed-cookie-test
export GLOBAL_IP_NAME=$BASE_NAME-ip
export BUCKET_NAME=$BASE_NAME-bucket
export BUCKEND_BUCKET=$BASE_NAME-bb
export LB_NAME=$BASE_NAME-lb
export PROXY_NAME=$BASE_NAME-proxy
export FORWARDING_RULE_NAME=$BASE_NAME-rule
export PROJECT_ID=hogehoge-project
gcloud config set project $PROJECT_ID

gcloud compute addresses create $GLOBAL_IP_NAME \
    --network-tier=PREMIUM \
    --ip-version=IPV4 \
    --global

gcloud compute backend-buckets create $BUCKEND_BUCKET \
  --gcs-bucket-name=$BUCKET_NAME \
  --enable-cdn

gcloud compute url-maps create $LB_NAME \
  --default-backend-bucket=$BUCKEND_BUCKET

gcloud compute target-http-proxies create $PROXY_NAME \
  --http-keep-alive-timeout-sec=10 \
  --url-map=$LB_NAME

gcloud compute forwarding-rules create $FORWARDING_RULE_NAME \
  --load-balancing-scheme=EXTERNAL_MANAGED \
  --network-tier=PREMIUM \
  --address=$GLOBAL_IP_NAME \
  --global \
  --target-http-proxy=$PROXY_NAME \
  --ports=80


URL 署名鍵の作成と登録

以下のコマンドで作成したCloud Loadbalancingに署名鍵を登録します。

export BASE_NAME=signed-cookie-test
export KEY_NAME=$BASE_NAME-key
export BUCKEND_BUCKET=$BASE_NAME-bb
export PROJECT_ID=hogehoge-project
gcloud config set project $PROJECT_ID

head -c 16 /dev/urandom | base64 | tr +/ -_ > key_file
export KEY=`cat key_file`

gcloud compute backend-buckets \
   add-signed-url-key $BUCKEND_BUCKET \
   --key-name $KEY_NAME \
   --key-file key_file

動作確認

上記で作成したLB, CDN , GCSの動作確認を行います。
作成したGCSはバケットが非公開状態ですので、LBに対してアクセスしても Access denied. が返ってきます。

export BASE_NAME=signed-cookie-test
export PROJECT_ID=hogehoge-project
gcloud config set project $PROJECT_ID

export GLOBAL_IP_NAME=$BASE_NAME-ip
export GLOBAL_IP=`gcloud compute addresses describe $GLOBAL_IP_NAME \
    --format="get(address)" \
    --global`

curl -X GET http://$GLOBAL_IP/index.html

アクセスできるようにするために、以下のPythonを実行して取得した署名付きCookieを使ってアクセスを行います。
以下のプログラムを signed-cookie.py と名前をつけて保存します。

from datetime import datetime, timedelta
import base64
import hmac
import hashlib
import sys

argv = sys.argv
if len(argv) != 4:
    print("usage: python3 ./signed-cookie.py $url_prefix $key_name $base64_key")
    exit()

url_prefix = argv[1]
key_name = argv[2]
base64_key = argv[3]
expiration_time=datetime.utcnow() + timedelta(minutes=30)

encoded_url_prefix = base64.urlsafe_b64encode(
    url_prefix.strip().encode("utf-8")
).decode("utf-8")
epoch = datetime.utcfromtimestamp(0)
expiration_timestamp = int((expiration_time - epoch).total_seconds())
decoded_key = base64.urlsafe_b64decode(base64_key)

policy = f"URLPrefix={encoded_url_prefix}:Expires={expiration_timestamp}:KeyName={key_name}"

digest = hmac.new(decoded_key, policy.encode("utf-8"), hashlib.sha1).digest()
signature = base64.urlsafe_b64encode(digest).decode("utf-8")

signed_policy = f"Cloud-CDN-Cookie={policy}:Signature={signature}"

print(signed_policy)

作成したプログラムを実行して結果を環境変数に入れます。
以下のような結果が出力されていればOKです。

export BASE_NAME=signed-cookie-test
export PROJECT_ID=hogehoge-project
gcloud config set project $PROJECT_ID
export GLOBAL_IP_NAME=$BASE_NAME-ip
export GLOBAL_IP=`gcloud compute addresses describe $GLOBAL_IP_NAME \
    --format="get(address)" \
    --global`
export KEY_NAME=$BASE_NAME-key
export KEY=`cat key_file`

export HEADER=`python3 ./signed-cookie.py http://$GLOBAL_IP $KEY_NAME $KEY`
echo $HEADER

Cloud-CDN-Cookie=URLPrefix=aHR0cDovLzM0LjEwMi4xOTUuMjA3:Expires=1709876030:KeyName=signed-cookie-test-key:Signature=dME1IqN49ntqeyvnwdVi1VAOvD0=

curlを実行します。
200 OKが返ってきますので、署名付きCookieを使ってindex.htmlへアクセスできるようになったことが確認できます。

export BASE_NAME=signed-cookie-test
export PROJECT_ID=hogehoge-project
gcloud config set project $PROJECT_ID
export GLOBAL_IP_NAME=$BASE_NAME-ip
export GLOBAL_IP=`gcloud compute addresses describe $GLOBAL_IP_NAME \
    --format="get(address)" \
    --global`

curl -H "Cookie: $HEADER" -I http://$GLOBAL_IP/index.html

後片付け

以下のリソースを作成していますので適宜削除します。
※forwarding-rulesを削除する際に私の環境だとなぜか止まったのですが、削除はできているようです。

  • クラウドリソース
    • Cloud Loadbalancing
    • Cloud Storage
    • Global IP
  • ローカルリソース
    • index.html
    • key_file
export BASE_NAME=signed-cookie-test
export GLOBAL_IP_NAME=$BASE_NAME-ip
export BUCKET_NAME=$BASE_NAME-bucket
export BUCKEND_BUCKET=$BASE_NAME-bb
export LB_NAME=$BASE_NAME-lb
export PROXY_NAME=$BASE_NAME-proxy
export FORWARDING_RULE_NAME=$BASE_NAME-rule

export PROJECT_ID=hogehoge-project
gcloud config set project $PROJECT_ID

gcloud compute forwarding-rules delete $FORWARDING_RULE_NAME --global
gcloud compute target-http-proxies delete $PROXY_NAME
gcloud compute url-maps delete $LB_NAME
gcloud compute backend-buckets delete $BUCKEND_BUCKET
gcloud compute addresses delete $GLOBAL_IP_NAME --global
gcloud storage rm -r gs://$BUCKET_NAME/index.html
gcloud storage buckets delete gs://$BUCKET_NAME

rm -i index.html
rm -i key_file

おわりに

Googleのドキュメントを読んでも分かりづらかったので実際に試して動くところまで確認できたのはよかったです。
これでCloud CDNの機能である署名付きCookieを実際に利用できました。
会員制サイトでコンテンツを配信するような場合に利用できるので、今後そのような場面があれば活用したいです。


GI Cloud は事業の拡大に向けて一緒に夢を追う仲間を募集しています

当社は「クラウドで日本のIT業界を変革し、世の中をもっとハッピーに」をミッションに掲げ、Google Cloudに特化した技術者集団として、お客様にコンサルティングからシステム開発、運用・保守まで一気通貫でサービスを提供しています。

まだ小規模な事業体ですが、スタートアップならではの活気と成長性に加えて、大手総合商社である伊藤忠グループの一員としてやりがいのある案件にもどんどんチャレンジできる環境が整っています。成長意欲の高い仲間と共にスキルを磨きながら、クラウドの力で世の中をもっとハッピーにしたい。そんな我々の想いに共感できる方のエントリーをお待ちしています。

採用ページ

※本記事は、ジーアイクラウド株式会社の見解を述べたものであり、必要な調査・検討は行っているものの必ずしもその正確性や真実性を保証するものではありません。

※リンクを利用する際には、必ず出典がGIC dryaki-blogであることを明記してください。
リンクの利用によりトラブルが発生した場合、リンクを設置した方ご自身の責任で対応してください。
ジーアイクラウド株式会社はユーザーによるリンクの利用につき、如何なる責任を負うものではありません。