今回は Cloudfront Functions でやってみました。もともと使っていたので、少しの追記で済みました1。
WAF との比較をまとめようかとも思ったのですが、比較している方がいらっしゃったのでそちらを見てください。
cloudfront function に送られてくるイベントの中身を調べるのに苦労しましたが、こことかを参考に作成。
terraform を使う場合のサンプルを抜粋して載せます。
Cloudfront について
Cloudfront はキャッシュを利用した CDN で、利用者はまずキャッシュサーバーにアクセスします。
リクエストしたコンテンツについて、キャッシュがあって TTL (Time To Live, キャッシュが残る時間) 以内のものはキャッシュが返されます。
キャッシュが無かったりTTLが切れたコンテンツへのアクセスはオリジンへの問い合わせがされます。
アクセスの流れは下図のようになります。
Cloudfront Functions について
Cloudfront Functions を使うと、上図の水色部分でデータの加工ができます。
キャッシュサーバーへのアクセス (viewer request) がインターネットからの入り口になります。
今回実装したいリファラー制限についても、viewer request で対処すればよいだろう、という算段です。
オリジンに直接アクセスされた場合は当然ながら Cloudfront Function は働かないので、
オリジンに対しても Cloudfront からのリクエストだけを許容するように設定しておくと良いですね。2
ソースコード
// viewer request
function handler(event) {
var request = event.request;
var uri = request.uri;
var headers = request.headers;
if (!(uri == '/')) {
if (!(headers['referer'] &&
headers['referer'].value.includes('target-domain.example.com'))) {
var response = {
statusCode: 403,
statusDescription: 'UnAuthorized',
};
return response;
}
}
// SPA のための処理
// 拡張子がある場合を除き、ルートにアクセスさせる
if (!uri.includes('.')) {
request.uri = "/index.html";
}
return request;
}
作るときはヘッダーの中身を調べるのに苦労しましたが、処理は単純なので関数も単純です。
おわりに
コンソールを使う場合は、Cloudfront のページの「関数」から関数を作って「発行」して、ディストリビューションの設定で関数の紐づけをすればよかったと思います。