security
A
信頼度ランク
| S | 公式ソース確認済み |
| A | 成功実績多数・失敗例少数 |
| B | 賛否両論 |
| C | 動作未確認・セキュリティリスク高 |
| Z | 個人所感 |
CloudFront署名付きURL vs 署名付きCookie — プライベートコンテンツ配信
CloudFrontの署名付きURL(Signed URL)と署名付きCookie(Signed Cookie)の違い、キーペアの作成、有効期限設定、S3との組み合わせ、OACによるS3直接アクセス禁止設定を解説。
一言結論
単一ファイルの一時公開には署名付きURL、複数ファイルへのまとめたアクセス制御には署名付きCookieを使うのが基本で、いずれもOACでS3への直接アクセスを封鎖して初めて意味を持つ。
プライベートコンテンツ配信の概要
CloudFrontでプライベートなS3コンテンツを配信する場合、S3を直接公開せずCloudFront経由でのみアクセスを許可する設計が必要だ。
❌ 悪い例(S3を直接公開):
ユーザー → S3直接アクセス(誰でもアクセス可能)
✅ 良い例(CloudFront + 署名付きURL):
ユーザー → CloudFront(署名検証) → S3(CloudFrontのみ許可)
↑
署名付きURLが必要
署名付きURL vs 署名付きCookie
署名付きURL:
→ 1つのファイルへのアクセスを制御
→ URLに署名を含む
→ 例: 動画ファイル1本のダウンロードリンク
署名付きCookie:
→ 複数ファイルへのアクセスを制御
→ Cookieに署名を含む(URLは変わらない)
→ 例: プレミアム会員が全動画にアクセス
→ RTMPディストリビューション(廃止)でも対応
使い分け:
単一ファイルのDL → 署名付きURL
複数ファイル・配信システム → 署名付きCookie
URLを変更したくない場合 → 署名付きCookie
Origin Access Control(OAC)の設定
S3バケットへの直接アクセスをブロックし、CloudFrontからのみ許可する。
# OACの作成
aws cloudfront create-origin-access-control \
--origin-access-control-config '{
"Name": "my-oac",
"OriginAccessControlOriginType": "s3",
"SigningBehavior": "always",
"SigningProtocol": "sigv4"
}'
# S3バケットポリシー(CloudFrontサービスプリンシパルのみ許可)
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "cloudfront.amazonaws.com"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-private-bucket/*",
"Condition": {
"StringEquals": {
"AWS:SourceArn": "arn:aws:cloudfront::123456789012:distribution/DISTRIBUTION_ID"
}
}
}
]
}
キーグループと署名者(Signer)
署名付きURL/Cookieの種類:
1. 信頼されたキーグループ(推奨):
→ CloudFrontキーグループにRSA公開鍵を登録
→ 秘密鍵はアプリサーバーで管理
→ IAMユーザーは不要
2. CloudFront キーペア(旧方式、非推奨):
→ AWSルートアカウントでキーペアを生成
→ セキュリティ上推奨しない
# RSA キーペアの生成
openssl genrsa -out private_key.pem 2048
openssl rsa -pubout -in private_key.pem -out public_key.pem
# CloudFrontに公開鍵を登録
aws cloudfront create-public-key \
--public-key-config '{
"Name": "my-signing-key",
"CallerReference": "my-signing-key-001",
"EncodedKey": "'$(cat public_key.pem | tr -d '\n')'"
}'
# キーグループを作成
aws cloudfront create-key-group \
--key-group-config '{
"Name": "my-key-group",
"Items": ["PUBLIC_KEY_ID"]
}'
署名付きURLの生成(Python)
from datetime import datetime, timedelta
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.serialization import load_pem_private_key
import base64
import json
def create_signed_url(url, private_key_path, key_pair_id, expiry_minutes=60):
"""CloudFront署名付きURLを生成"""
expiry = int((datetime.utcnow() + timedelta(minutes=expiry_minutes)).timestamp())
policy = json.dumps({
"Statement": [{
"Resource": url,
"Condition": {
"DateLessThan": {"AWS:EpochTime": expiry}
}
}]
}, separators=(',', ':'))
# 署名の生成
with open(private_key_path, 'rb') as f:
private_key = load_pem_private_key(f.read(), password=None, backend=default_backend())
signature = private_key.sign(policy.encode('utf-8'), padding.PKCS1v15(), hashes.SHA1())
encoded_signature = base64.b64encode(signature).decode('utf-8')
encoded_signature = encoded_signature.replace('+', '-').replace('=', '_').replace('/', '~')
encoded_policy = base64.b64encode(policy.encode('utf-8')).decode('utf-8')
encoded_policy = encoded_policy.replace('+', '-').replace('=', '_').replace('/', '~')
signed_url = f"{url}?Policy={encoded_policy}&Signature={encoded_signature}&Key-Pair-Id={key_pair_id}"
return signed_url
# 使用例(boto3 CloudFrontSignerを使う方が実用的)
import boto3
from botocore.signers import CloudFrontSigner
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
def rsa_signer(message):
with open('private_key.pem', 'rb') as f:
private_key = load_pem_private_key(f.read(), password=None)
return private_key.sign(message, padding.PKCS1v15(), hashes.SHA1())
cf_signer = CloudFrontSigner('KEY_PAIR_ID', rsa_signer)
signed_url = cf_signer.generate_presigned_url(
url='https://d1234.cloudfront.net/videos/movie.mp4',
date_less_than=datetime.utcnow() + timedelta(hours=1)
)
署名付きCookieの設定
# 署名付きCookieの生成
signed_cookies = cf_signer.generate_signed_cookies(
url='https://d1234.cloudfront.net/premium/*', # ワイルドカード可
date_less_than=datetime.utcnow() + timedelta(hours=24)
)
# HTTP レスポンスにCookieをセット
response.set_cookie('CloudFront-Policy', signed_cookies['CloudFront-Policy'])
response.set_cookie('CloudFront-Signature', signed_cookies['CloudFront-Signature'])
response.set_cookie('CloudFront-Key-Pair-Id', signed_cookies['CloudFront-Key-Pair-Id'])
試験頻出ポイント
| シナリオ | 回答 |
|---|---|
| 単一動画ファイルの一時ダウンロードリンク | 署名付きURL |
| プレミアム会員が全コンテンツにアクセス | 署名付きCookie |
| S3への直接アクセスをブロック | OAC(Origin Access Control) |
| 有効期限付きアクセス制御 | 署名付きURL(DateLessThan条件) |
| IPアドレスでのアクセス制限 | 署名付きURL(IpAddress条件) |
まとめ
CloudFrontの署名付きURLは単一ファイル、署名付きCookieは複数ファイルのアクセス制御に適している。OACでS3の直接アクセスを禁止し、CloudFront経由のみを許可する構成が現在の推奨設計だ。