期限付きURL
今回の要件としては、URLを時限式でexpireさせる事で、URLを叩けばいくらでもアクセス出来るのを防ぐ事を目的としています。
AWS構成
事前準備
CloudFrontのOriginをS3に向けてあり、S3とCloudFrontのURLどちらを叩いても同じ内容が表示されている状態にしてあります。
テスト用に、Windows 7に標準で入ってる画像をアップロードしてあるので、アクセスしてみましょう。
どちらも同じ内容が表示されます。
実装
構想
期限付きのURLをEC2から発行し、CloudFrontを経由してS3からコンテンツを取得し配信する。CDP:Private Cache Distributionパターンというやつらしいです。
CloudFront、S3の設定変更
今はただアクセスするだけで表示されてしまうので、設定を変更し、署名付きのURLでないとアクセスが出来ないように、AWSコンソールから各設定の変更を行います。
CloudFront
まずはCloudFrontの Originsタブ から、基本設定を変更します。
- Restrict Bucket Access
- YesにするとS3に対するアクセス制限をかけられる
- Origin Access Identity
- CloudFrontがS3にアクセスするために必要なID Yesにすると新規作成
- Grant Read Permissions on Bucket
- YesにするとS3の設定(Bucket Policy)をCloudFrontから自動で書き換える
この一番下の、 Restrict Viewer Access(Use Signed URLs) が今回のキモとなる設定です。
これを「Yes」にすると、新しい設定項目が出てきます。
- Trusted Signers
- 説明:Choose whether you want to use the current AWS account and/or other AWS accounts to create signed URLs.
うーん。。。Specify Accountsに変更するシーンってどんなのだろう?
大規模で他社が絡む開発とか?何かしらの政治的な理由?うーむ・・・
とりあえず、同一アカウントから署名付きURLを発行するので、Selfとして設定完了です。
反映されるまで、少し時間がかかるようです。CloudFrontの表示を更新して、署名キーが無いため表示されない事を確認。
CloudFrontの方はこれで設定完了です。
S3
S3の方はまだ見られる状態なので、いくらCloudFront経由のアクセスに制限がかけられても、S3に直でアクセスされたら普通に表示されてしまうためアウトです。
バケット名を右クリックして Properties を開いて、Edit bucket policy で設定を変更します。
すると Bucket Policy Editor が開かれて、JSON形式の設定情報が表示されます。
{ "Version": "2008-10-17", "Id": "Policy1391746695837", "Statement": [ { "Sid": "Stmt1391746693957", "Effect": "Allow", "Principal": { "AWS": "*" }, "Action": "s3:GetObject", "Resource": "arn:aws:s3:::s3-********.cloudpack.jp/*" }, { "Sid": "2", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity E************8" }, "Action": "s3:GetObject", "Resource": "arn:aws:s3:::s3-********.cloudpack.jp/*" } ] }
Statement
の14行目以下が、CloudFrontの設定変更により自動で付け加えられた設定でしょう。おそらく上位優先でアクセスの許可が下りてしまい、まだ表示が可能のようですね。
という事で、マークが付いてる5〜13行目の設定を消して設定をすると・・・
403 Forbidden。これで普通にアクセスするだけで表示する事は出来なくなりました。
CloudFront 暗号化鍵
署名付きURLを作成するために、CloudFront用のキーペアを取得する必要があります。
アカウントのプルダウンメニューから Security Credentials をクリックして、CloudFront Key Pairs のメニューから、Create New Key Pair をクリック。
秘密鍵(pk-xxxx.pem)、公開鍵(rsa-xxxx.pem)共にダウンロードしておきましょう。公開鍵は別にダウンロードしなくてもいいのかな?以後登場しません。
で、秘密鍵はEC2内のどこかに適当に配置しておきましょう。
開発
さて、では準備が完了したので SDKの説明書き を参考に開発を進めます。といっても、署名付きURLを作る、というための項目が既に用意されていますね。
Signing CloudFront URLs for Private Distributions を見て、下記の通り10行足らずで実装可能でした。
'/path/to/keys/pk-'.$_cf_accesskey_id.'.pem', 'key_pair_id' => $_cf_accesskey_id, )); //期限付きURL作成 $signedUrlCannedPolicy = $cloudFront->getSignedUrl(array( 'url' => 'http://'.$_cf_url . '/' . 'Tulips.jpg?u='.uniqid(), 'expires' => strtotime('+1 minutes'), )); echo $signedUrlCannedPolicy;
ソースのポイントとしては、
$_cf_accesskey_id
が、ダウンロード直後の秘密鍵ファイル名とキーペアIDと一緒だから、ダウンロードしたのをそのまま任意のディレクトリに置けば良い- 期限付きURLにランダムな値をパラメータとして付与する事で、実行の度に別のURLを作成出来る
- 動画をストリーミングで取得する場合は、
$cloudFront->getSignedUrl()
に渡すURLのプロトコルを 'rtmp://' とすれば良い
という感じでしょうか。
実行
先のソースを key.php
として保存して実行。
$ php key.php http://dxxxxxxxxxxxxh.cloudfront.net/Tulips.jpg?u=54b8b43a36d99&Expires=1421390966&Signature=Sr~Acz4PNzDrYjnys1xKsMCo-Q94jj3BkkzSyCqAR8p4P4qHfaJ9M3knlUk0l9WdnM2FFO5r2vqapLIbFqmwP8yUznXufPD15aCOdsf~aJS61YV4Y5DMVJaKfX1YBJJpohfXNYSZWGyNPDKFtEn3jWzgQvAPo283W41eh7DYxvD-s-XqBQp327ejcashvdJnAwWnSLVTAjvJViZafbJ-20yHpTq2nc-FFYxsQazkL47X7mQCIIvl8gFPBAoBma8RH~uP-GiPqBUdTFpX51AFCX1h2xOVFI5R9ZY-lISPTbw0E~EG-GlmpC~l0-fTGA677g4xTnNMiAhx51QyeD16sg__&Key-Pair-Id=AxxxxxxxxxxxxxxxxxxQ
なっがーいURLが出力されました。
上記が署名&期限付きのURLとなります。
では早速アクセスしてみましょう。
表示されました!
では、先のソースでは strtotime('+1 minutes')
として、1分間の期限を設けていたので、1分待ってもう一度アクセスしてみましょう。
うんうん、アクセス拒否のXMLが出力されました。期限付きURLはちゃんと機能していますね。
余談
CloudFrontの設定で、パラメータを無視してキャッシュを保存出来る機能ってあったような…?
あれば、期限付きの毎回異なるURLを出力しながら、データの配信にはキャッシュを利用して負荷軽減が出来たりとかするのかな。
これはこれで別で調べてみよう!
元記事はこちらです。
「S3+CloudFrontで期限付きURL」