📚

はじめてのCloudflare Images その4 Image Resizing のセキュリティを考える。

2023/04/11に公開

前回の記事ではImage Resizingを紹介しました。URLパラメータで動的に画像がリサイズされ配信される機能です。
ストレージにはオリジナル画像のみが保存され、リサイズされた画像は自動でキャッシュされるため、あらかじめ複数パターンの画像を用意しておく必要がなく、ストレージ容量を節約できます。

前回のおさらいですが、

https://imageresize.harunobukameda.labrat.online/cdn-cgi/image/width=300,height=300/https://imageresize.harunobukameda.labrat.online/icons/apache_pb2.gif

で幅横ともに300ピクセルの画像が配信されますが、このwidth,heightの値を変更すると画像が動的にリサイズされます。

これはとても便利ですが商用で利用することを考えると2点問題があります。

  1. "https://imageresize.harunobukameda.labrat.online/icons/apache_pb2.gif" 部分は実は設定次第で外部URLであればなんでも動作してしまう。
  2. ユーザーが画像を勝手に変換(width,heightの値を自由に変更可能)できてしまったりするため、"imageresize.harunobukameda.labrat.online"ドメインを隠匿化させサーバサイドで画像変換処理を呼び出したい。

それぞれCloudflareでは解決方法が提供されていますのでご説明します。

呼び出しオリジンの制御"
例えば以下のようオリジンを無視して呼び出すことが設定によっては可能です。

https://imageresize.harunobukameda.labrat.online/cdn-cgi/image/width=300,height=300/https://www.cloudflare.com/static/4126c00edfef8927538de0a1c04ec715/cloud-multi-600x356-c1bc40d.png

これはデモ環境として意図的に可能としていますが、デフォルトではImage Resizingは以下のオプションがオフとなっています。

オフとしておくことで、呼び出し元ドメインがCloudflareでProxyモードで管理されていることを前提として、当該ドメインでしか動作しなくなります。今回の例でいうと"imageresize.harunobukameda.labrat.online"に存在している画像のみが変換可能な対象となります。
商用環境で使う際は必ずこのオプションをオフにしておいてください。

URLの隠匿化
こちらのドキュメントにはWorkersを用いることで画像変換の呼び出し、今回の環境でいうと"https://imageresize.harunobukameda.labrat.online/" というドメインを隠匿化させることができます。ドキュメントのサンプルスクリプトだとドメインを隠匿化しておらず目的を果たさないため、以下に改造かつ超超シンプルにしたWorkersスクリプトサンプルを記載します。

addEventListener("fetch", event => {
	event.respondWith(handleRequest(event.request))
  })
  
  async function handleRequest(request) {

	let url = new URL(request.url)
	const path = url.pathname // "/width=300&image=/icons/apache_pb2.gif"
	const params = path.split("&") // ["width=300", "image=/icons/apache_pb2.gif"]
	const width = params[0].split("=")[1] // "300"
	const imageURL = params[1].split("=")[1] // "/icons/apache_pb2.gif"

	let imageresize;
	imageresize = "https://imageresize.harunobukameda.labrat.online/cdn-cgi/image/width=200" + imageURL;

	// Returning fetch() with resizing options will pass through response with the resized image.
	return fetch(imageresize);
  }

こうすることによって、画像変換処理がサーバサイドで実行されるようになり、ドメインを隠匿化させることが可能です。私の環境では"image-resize.harunobukameda.workers.dev"でWorkersをデプロイしていますが、以下の呼び出しで動作します。

https://image-resize.harunobukameda.workers.dev/width=300&image=/icons/apache_pb2.gif

(この環境はテスト用にしばらく残しておきます)
ただしユーザーが勝手に変更できないようにwidthの値が何であれWorkers内部スクリプトで固定値で上書きしています。あとは必要に応じていろいろカスタマイズしてください。

Discussion