この記事は Enabling URL token validation : https://docs.fastly.com/guides/tutorials/enabling-url-token-validation
を元に作成しています。最新の情報についてはdocsの情報をご参照下さい。
FastlyでURL token 認証を利用する では特定のURLを認証するTokenをクエリストリングでFastlyに送るサンプルを作成しましたが、今回はもう少し実際のサービスでもありそうな事例を考えて、それにあった設定を作成してみます。
要件としては以下の様なものと仮定します。
・TokenはクエリストリングではなくHTTP リクエストヘッダー token
に含めてクライアントからFastlyに送信
・Token は特定の URL ではなく、特定の Path 配下で同じものが有効となる
・認証対象は特定 Path 配下の画像のみとする
具体的には
http://www.example.com/token_auth/usera/index.html
http://www.example.com/token_auth/usera/image01.jpg
http://www.example.com/token_auth/usera/image02.jpg
http://www.example.com/token_auth/userb/index.html
http://www.example.com/token_auth/userb/image01.jpg
http://www.example.com/token_auth/userb/image02.jpg
というような構成のページがあるとして、useraのTokenを持つユーザーは同一のTokenで /token_auth/usera/ 配下の画像をすべて見ることが出来るが、/token_auth/userb/ パス配下の画像にはアクセス出来ない、というイメージを想定しています。
おさらいですが、FastlyのToken認証の基本的な機能は以下となります。
「Token認証を利用することで、特定の期間のみアクセス可能なURLを作成することが出来ます。TokenはWebアプリケーション側で作成され、クエリストリングの形でユーザーに提供されます。リクエストはFastlyサーバーで認証され、Tokenが正しい場合のみコンテンツが配信されます。指定した期間を経過するとTokenは無効になります。」
それでは早速VCLのコードを書いていきたいと思います。コードはクエリストリングでTokenを渡すコードをもとにして、必要な部分を一部修正しています。
Token認証用のVCLコード
VCLに自分でコードを直接記載したい場合は、Snippet、またはカスタムVCLを利用しますが、特別な理由がないのであれば手軽なSnippetの利用をお勧めします。Token認証の場合はvcl_recv
内に以下のようなコードを記載します。
# only do this once per request
if (req.url ~ "^/token_auth" && req.url.ext ~ "(?i)^(gif|png|jpg|jpeg|webp)$" && req.request != "FASTLYPURGE" && fastly.ff.visits_this_service == 0 && req.restarts == 0) {
declare local var.token STRING;
declare local var.token_expiration STRING;
declare local var.token_signature STRING;
declare local var.to_sign STRING;
declare local var.token_path STRING;
# extract token from request header
set var.token = req.http.token;
# make sure there is a token
if (var.token == "") {
error 403;
}
# make sure there is a valid expiration and signature
if (var.token !~ "^(\d{10,11})_([a-f0-9]{40})$") {
error 403;
}
# extract token expiration and signature
set var.token_expiration = re.group.1;
set var.token_signature = re.group.2;
# extract the path to authenticate from req.url
set var.token_path = req.url.dirname;
# calculate string to sign
set var.to_sign = var.token_path + var.token_expiration;
# make sure the signature is valid
if (!digest.secure_is_equal(var.token_signature, regsub(digest.hmac_sha1(digest.base64_decode("mysecret"), var.to_sign), "^0x", ""))) {
error 403;
}
# make sure the expiration time has not elapsed
if (time.is_after(now, std.integer2time(std.atoi(var.token_expiration)))) {
error 410;
}
}
上記のサンプルVCLコード内のmysecretkey
は独自のシークレットキー(秘密の文字列)に変更し、外部にもれないように管理して下さい。シークレットキーが外部にもれると、Tokenを生成して認証を突破することが可能になります。
TokenはリクエストにHTTPリクエストヘッダー token
の値としてリクエストに付与する必要があります。Tokenの値のフォーマットは[expiration]_[signature]
となり、具体的には以下のようなフォーマットとなります。
1441307151_4492f25946a2e8e1414a8bb53dab8a6ba1cf4615
VCLでの処理内容
VCLでの処理について上から順に説明していきます。
if (req.url ~ "^/token_auth" && req.url.ext ~ "(?i)^(gif|png|jpg|jpeg|webp)$" && req.request != "FASTLYPURGE" && fastly.ff.visits_this_service == 0 && req.restarts == 0) {
リクエストが/token_auth 配下、かつ拡張子がgif, png, jpg, jpeg, webpの場合に Token 認証の対象としています。
# extract token from request header
set var.token = req.http.token;
クエリストリングではなく、リクエストヘッダーに設定された token
から取得した値を Token とします。
# extract token expiration and signature
set var.token_expiration = re.group.1;
set var.token_signature = re.group.2;
その後 Token が存在するかやフォーマットを確認し、問題がなければ有効期限とsignitureを変数に代入します。
# extract the path to authenticate from req.url
set var.token_path = req.url.dirname;
認証用のパス情報を var.token_path に保存します。ここではreq.url.dirname
を利用して、リクエストURLの最後の "/" の手前までを取得しています。
この処理の結果、
/token_auth/usera/image01.jpg
/token_auth/usera/image02.jpg
などのURLの場合、var.token_path
には同一の /token_auth/usera
が保存されます。
その後はリクエストに付与されていたSignatureとVCLで生成する文字列が同一かどうかを判断します。
その際に req.url
ではなくvar.token_path
を利用している点以外は通常の認証する処理と同じになります。
アプリケーション側で必要な設定
Fastlyを利用してToken認証を実施するためには、アプリケーション(Webサーバー)側でTokenを生成するためのコードを追加する必要があります。Fastlyでは各種言語でのTokenコードを生成するコードのサンプルをGitHub上で提供しています。これらのコードの利用してTokenを生成して下さい。
ポイントとしてはTokenを生成する際に指定するPathを、上記のVCLでvar.token_path
に保存するPathと同じものにして下さい。Pathの内容が異なっていると生成されるSignatureも異なりますので認証がエラーとなります。
Token を生成したら、生成されたTokenをクライアントのアプリケーションなどから token
という名前の HTTP リクエストヘッダーとしてFastlyサーバーに送信するようにして下さい。
Token認証のテスト
テストは以下のようなコマンドを利用して行うことが可能です。生成したTokenをリクエストに設定して意図した通りに認証が行われるかを確認して下さい。
curl -svo /dev/null -H "token:<generated_token>" http://<your_domain>/token_auth/usera/image1.jpg
curl -svo /dev/null -H "token:<generated_token>" http://<your_domain>/token_auth/usera/image2.jpg
curl -svo /dev/null -H "token:<generated_token>" http://<your_domain>/token_auth/userb/image1.jpg
curl -svo /dev/null -H "token:<generated_token>" http://<your_domain>/token_auth/userb/image2.jpg
まとめ
今回の記事ではtoken情報をHTTPリクエストヘッダーという形で送信していますが、もちろん別の方法(例えばCookieで渡したり、リクエストのパスに加えるなど)でtokenを送信することも可能です。
また、認証対象の範囲などもVCLを利用して自由にコントロールすることが出来ます。必要な要件に応じて設定を作成してみて下さい。