🖼️

CloudFrontで画像最適化CDNを構築した際の「+α」の共有

2024/05/29に公開

はじめに

CloudFrontで画像最適化を行うCDNを構築したので、その知見の共有です。

構成としては「CloudFrontを前段に、Lambda@Edge(Node.js)でsharpというライブラリを動かす」というもので、そのCDNに対して、TANOMUという、弊社で開発してるBtoBのSaaSからリクエストが飛ぶ、という流れです。

この構成に関する記事はインターネットにたくさんあるので、今回はより細かい「+α」を共有します。

AWSのブログによる、同様の構成の紹介は下記です。
https://aws.amazon.com/jp/blogs/news/resizing-images-with-amazon-cloudfront-lambdaedge-aws-cdn-blog/

この記事では、上記のAWSのブログとの差に焦点を当てて、解説します。

本題

Viewer-Request関数にて、CloudFront Functionsを利用する

上記のAWSのブログでは、(おそらくCloudFront Functionsがまだ無かったためか)Viewer-Request関数でLambda@Edgeを利用していますが、今回はCloudFront Functionsを利用するようにしました。

CloudFront Functions利用に合わせて、後述する署名で用いるシークレットの保存・呼び出しに、CloudFront KeyValueStoreを利用しました。

今回はAWS CDKで構築したのですが、CloudFront KeyValueStoreを利用することで、コードにシークレットを記述せずにすみ、また「CLIから実行時にシークレットを渡す」のような追加の実装も必要ない、というメリットがありました。

Viewer-Request関数にて、署名の検証をする

アプリ側でURLとパラメータから署名を生成して、Viewer-Request関数でその署名を検証することで、アプリ以外からのリクエストを弾くようにしました。

今回は、後述するようにキャッシュバージョンをクエリパラメータに乗せるようにしたので、攻撃者がキャッシュバージョンを無限にインクリメントすることで、無限に新しい画像最適化を実行させることが可能だったため、署名の検証でこれを防ぐようにしました。

キャッシュのバージョンをクエリパラメータに乗せる

画像最適化の結果、想定外の出力がされた場合に、キャッシュをクリアできるように、クエリパラメータでキャッシュのバージョン番号を指定するようにしました。

想定してる流れは、下記のようになります。

  1. ブラウザから xxx.jpg?cache=1にリクエストが行われる
  2. Lambad@Edgeにて画像最適化が行われ、/xxx_cache-1.jpg というパスで、S3に保存される
  3. CloudFrontでキャッシュが保存されたうえで、ブラウザに返却される
  4. ブラウザでキャッシュが保存されたうえで、画像が描画される

ここで、描画された画像になんらかの異常があった場合、

  1. アプリ側でキャッシュバージョンをインクリメント
  2. ブラウザから xxx.jpg?cache=2にリクエストが行われる
  3. Lambad@Edgeにて画像最適化が行われ、/xxx_cache-2.jpg というパスで、S3に保存される
  4. (以下おなじ)

とすることで、キャッシュクリアを容易にすることができます。

Origin-request関数にて、Lambda@Edgeで、画像最適化を行う

上記のAWSのブログでは、Origin-Response関数でLambda@Edgeを呼んでいますが、今回はOrigin-Request関数で、Lambda@Edgeを呼ぶようにしました。

その理由としては、Lambad@Edgeの制限として、「オリジンレスポンスイベント」の「ヘッダーと本文を含む、Lambda 関数によって生成されたレスポンスのサイズ」が1MBですが、アプリの仕様として画像サイズの上限はそれを超える、としていたためです。
https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/cloudfront-limits.html#limits-lambda-at-edge

そこで、Origin-Request関数で「指定されたURIに最適化後の画像が無ければ、最適化後の画像を生成して、対象のURIに保存」とすることで、この制約を回避しました。

TANOMU

Discussion