【実務・中級編】 GCP Cloud CDNの署名付きURL(Signed URLs)と署名付きCookie(Signed Cookies)によるアクセス制御 – クラウドインフラと仮想化ネットワーク実践ガイド

GCP Cloud CDNの「署名付きURL/Cookie」を攻略する:コンテンツ保護の最前線

こんにちは。クラウドインフラを渡り歩いてきたSREとして、これまで数多のシステム障害やセキュリティインシデントと向き合ってきました。

Webサービスを展開する際、避けて通れないのが「有料コンテンツや機密性の高いアセットをどう守るか」という問題です。S3やGCSのバケットを「公開」にしてCDNを被せれば爆速で配信できますが、そのままでは誰でもURLを知っていればアクセスできてしまいます。

そこで登場するのが Cloud CDNの「署名付きURL(Signed URLs)」と「署名付きCookie(Signed Cookies)」 です。今回は、教科書的な仕様説明はそこそこに、現場で泥臭くハマりやすいポイントや、実装の勘所を技術者の視点で紐解いていきます。

—

そもそも、なぜ署名が必要なのか?

CDNは「キャッシュサーバー」です。オリジン(GCSやロードバランサのバックエンド)へ行く前のエッジでコンテンツを返却するため、オリジン側で認証をかけてもエッジでキャッシュされてしまっては意味がありません。

このジレンマを解決するのが、「URLやCookieの中に、サーバーが発行した暗号署名を埋め込む」という発想です。これにより、CDNエッジ側で「このリクエストは正当なユーザーか?」を計算コストを抑えつつ高速に判定できます。

署名付きURL vs 署名付きCookie:使い分けの判断基準

  • 署名付きURL: 個別のファイル(動画、PDF、画像)へのアクセス制御に最適。静的アセットをピンポイントで守りたい場合に適しています。
  • 署名付きCookie: サイト全体のディレクトリや、大量のセグメント化された動画配信(HLS/DASH)を守る場合に適しています。いちいち全URLを書き換えるのは非効率ですからね。

—

署名付きURLの仕組み:裏側で何が起きているか

署名付きURLは、主に Signature、Expires、KeyName というパラメータで構成されます。

1. サーバー側: 秘密鍵を使って、URLのパスと有効期限をハッシュ化(HMAC-SHA256)し、署名を生成。
2. クライアント側: 署名が付与されたURLでリクエストを投げる。
3. CDNエッジ: 自身が持つ公開鍵(または共有鍵)で署名を検証。一致すればコンテンツを返す。

実装の勘所:Pythonでの署名生成例

以下は、Google Cloudの公式ライブラリを使わずとも理解できる、署名生成のロジックです。

import base64
import hashlib
import hmac
import time

# 秘密鍵(GCPのロードバランサ設定で保存されているものと一致させる)
secret_key = b'your-super-secret-key-that-is-base64-encoded'
url_to_sign = "https://cdn.example.com/videos/secret-movie.mp4"
expiration = int(time.time()) + 3600  # 1時間有効

# 署名の生成
# 注意: URLのパス部分に対して署名を行うのが基本です
def sign_url(url, key, exp):
    # HMAC-SHA256で生成
    signature = hmac.new(key, msg=url.encode(), digestmod=hashlib.sha256).digest()
    # Base64でエンコードしてURLセーフに
    encoded_signature = base64.urlsafe_b64encode(signature).decode()
    return f"{url}?Expires={exp}&KeyName=my-key&Signature={encoded_signature}"

print(sign_url(url_to_sign, secret_key, expiration))

—

現場で必ずハマる「罠」とトラブルシューティング

私がこれまで現場で見てきた「署名付きURLが動かない」というトラブルの9割は、以下のいずれかです。

1. URLのエンコードミス

署名に使う文字列と、ブラウザがリクエストするURLが完全に一致していなければなりません。クエリパラメータの順序や、URLエンコードされた文字(%20など)の扱いが微妙に違うだけで、署名検証は即座に 403 Forbidden を返します。

Tips: 署名を生成する際の URL は、リクエストされるURLの「パス部分」のみを正確に計算対象にしてください。

2. 秘密鍵のローテーション

セキュリティ要件が厳しいプロジェクトでは鍵の更新(ローテーション)が必須です。
Cloud CDNでは「署名鍵」を複数保持できます。KeyNameを正しく指定していないと、古い鍵で検証しようとして失敗します。デバッグ時は、どの鍵を使っているか必ず確認しましょう。

3. キャッシュと認証の競合

Cache-Control ヘッダーの設定ミスです。public キャッシュを許可しつつ、署名で制御するという構成は非常に強力ですが、誤ったキャッシュ制御を行うと「署名が切れても古いキャッシュが見えてしまう」あるいは「逆にキャッシュが効かない」という状態に陥ります。

—

運用SREとしての推奨設定

実務では、以下の構成を強く推奨します。

1. URL署名鍵の管理: 秘密鍵は絶対にコードにハードコーディングせず、Secret Manager で管理してください。
2. 署名の検証ログ: Cloud Logging(旧Stackdriver)で、403 エラーが発生した際の「原因」を特定できるようにしておきます。CDNのログには、検証失敗の理由(signature_mismatch や expired など)が記録されることがあります。
3. HLS/DASH配信時の注意: 動画配信の場合、マニフェストファイル(.m3u8)だけでなく、その後の動画チャンク(.ts)一つ一つにも署名が必要です。これには、マニフェスト自体に署名付きURLを埋め込むか、署名付きCookieを利用してセッション全体を保護するのが現実的です。

—

最後に:ネットワークは「正直」である

ネットワークエンジニアとしていつも思うのは、「パケットは嘘をつかない」ということです。署名付きURLが弾かれるとき、そこには必ず論理的な理由があります。

まずは curl -v を使って、実際にエッジが返している X-Cache ヘッダーやレスポンスコードを凝視してください。

# デバッグ用コマンド
curl -I "https://cdn.example.com/protected.jpg?Expires=1700000000&KeyName=key1&Signature=..."

もし 403 Forbidden が返ってきたら、それは暗号の計算が合っていないか、鍵が間違っているという「事実」を教えてくれています。泥臭く一つずつ切り分けていけば、必ず解決できます。

皆さんのクラウドインフラ構築が、より堅牢で、かつエレガントなものになることを願っています。質問があれば、ぜひコメント欄で教えてください。現場の知見を共有しましょう。

コメント

タイトルとURLをコピーしました