FastlyでURL token 認証を利用する 1


:::note info
この記事では Secret と 時間を利用したシンプルな Token を利用する手順を説明しています。同様の機能を JSON Web Token(JWT) を利用して実現することも可能です。詳細は以下のページをご参照下さい。
https://developer.fastly.com/solutions/examples/json-web-tokens/
:::


Token 認証を利用することで、特定の期間のみアクセス可能な URL を作成することが出来ます。Token は Web アプリケーション側で作成され、クエリストリングの形でユーザーに提供されます。
リクエストは Fastly サーバーで認証され、Token が正しい場合のみコンテンツが配信されます。
指定した期間を経過すると Token は無効になります。


## Token 認証用の VCL コード
Token 認証を利用するためには Snippet、またはカスタム VCL を利用して、vcl_recv内に以下のようなコードを記載しますが、特別な理由がなければ Snippet の利用をお勧めします。




```:vcl_recv
# only do this once per request
if (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;

  # extract and remove the token
  set var.token = querystring.get(req.url, "token");
  set req.url = querystring.filter(req.url, "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;

  # calculate string to sign
  set var.to_sign = req.url + var.token_expiration;

  # make sure the signature is valid
  if (!digest.secure_is_equal(var.token_signature, regsub(digest.hmac_sha1(digest.base64_decode("YOUR%SECRET%KEY%IN%BASE64%HERE"), 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コード内の`YOUR%SECRET%KEY%IN%BASE64%HERE` は独自のシークレットキー(秘密の文字列)に変更し、外部にもれないように管理して下さい。シークレットキーが外部にもれると、Tokenを生成して認証を突破することが可能になります。

シークレットキーは任意の文字列で問題ありません。`openssl rand -base64 32`を利用して作成することも可能です。

Tokenはリクエストにクエリストリングとして `?token=` の形で付与する必要があります。Tokenの値のフォーマットは`[expiration]_[signature]`となり、具体的には以下のようなフォーマットとなります。

    1441307151_4492f25946a2e8e1414a8bb53dab8a6ba1cf4615

このTokenをもつリクエストの完全なURLとしては以下のようになります。

    http://www.example.com/foo/bar.html?token=1441307151_4492f25946a2e8e1414a8bb53dab8a6ba1cf4615

### 上記のVCLで行われている処理について

上述のVCLのコードでは2つの処理を行っています:

* Tokenに指定された有効期限の確認
* SignatureがTokenで指定されたものと一致するか

Signatureが無効な場合、リクエストに対してはHTTPのレスポンスコードとして403 Forbiddenが返却されます。Signatureは有効だがTokenに設定された有効期限が切れている場合、410のレスポンスコードが返却されます。返却されたレスポンスコードの違いから認証エラーの原因を確認することが出来ます。


## アプリケーション側で必要な設定

Fastlyを利用してToken認証を実施するためには、アプリケーション(Webサーバー)側でTokenを生成するためのコードを追加する必要があります。Fastlyでは各種言語での[Tokenコードを生成するコードのサンプル](https://github.com/fastly/token-functions)をGitHub上で提供しています。これらのコードの利用してTokenを生成し、Token認証対象のリクエストパスにクエリストリングとしてTokenを付与して下さい。


## Token認証のテスト
Token認証のテストを行うには、以下のようなフォーマットでアプリケーション側で生成したTokenをクエリストリングの形でURLに付与して下さい。

`http://www.example.com/foo/bar.html?token=1441307151_4492f25946a2e8e1414a8bb53dab8a6ba1cf4615`

Tokenが有効な場合は通常通りにレスポンスが返却されます。Tokenが不正な場合などは403レスポンスなどが返却されます。


### NULバイトに関する注意事項

シークレットキーにNULバイトを含まないことを確認して下さい。base64-decoded文字列がNULバイト(0x00)を含む場合、そのバイトとそのバイト以降のバイトはレスポンスに含まれません。詳細については [base64 decoding](https://docs.fastly.com/guides/vcl/cryptographic-and-hashing-related-vcl-functions#base64-decoding)をご参照下さい。

---


###  Purge を 認証の対象外に設定

標準のVCLでは URL 単位のキャッシュの削除（URL Purge）リクエストも認証の対象になっています。
パージリクエストを処理対象外にしたい場合は、最初の if 条件に `req.request != "FASTLYPURGE"` を追加します。

```:vcl_recv
# only do this once per request
if (req.request != "FASTLYPURGE" && fastly.ff.visits_this_service == 0 && req.restarts == 0) {

# この後の処理は同じです

}

```


## まとめ
本記事では特定のURLに対して有効なTokenをクエリストリングで渡すサンプルでした。[FastlyでURL token 認証を利用する ２](https://qiita.com/AtsushiFukuda/items/dd21329d6035ac5e3891)では、特定ディレクトリ配下に対して有効なTokenをHTTP リクエストヘッダーとして送信する設定を説明します。