はじめに
Webアプリケーション開発において、画像管理は避けては通れない課題です。
最適化された画像の提供はユーザー体験の向上だけでなく、パフォーマンスの最適化にもつながります。
画像のリサイズやフォーマット変換を行うためには、適切なツール・インフラの選定が必要です。
この記事では、imgproxyという画像プロキシサーバーを使った画像処理について紹介します。
imgproxyとは
imgproxyは、高速でセキュアなオープンソースの画像プロキシサーバーで、URLパラメータを利用して、画像のリサイズやフォーマット変換などの処理を行います。
Go言語と画像処理ライブラリlibvipsを使って実装されています。
起動方法
本記事では公式提供されているDockerイメージとdocker compose コマンドを使ってimgproxyを起動します。
下記のymlファイルを用意してください。
services:
imgproxy:
image: ghcr.io/imgproxy/imgproxy:latest
ports:
- "8080:8080"
ymlファイルの準備ができたら、docker composeでコンテナを起動しましょう。
docker compose up
ブラウザで http://localhost:8080 にアクセスして下記のようなページが表示されれば起動成功です。
実際に使ってみる
imgproxyを使って画像をリサイズしてみましょう。
- 使用する画像URLはこちら → URL
- Nasaが公開している画像(7680x4320)
- imgproxyの公式ドキュメントでも使用されています
上記の画像を300x300にリサイズします。
imgproxyコンテナを起動させた状態で、下記にアクセスしてください。
http://localhost:8080/insecure/rs:fill:300:300/plain/https://mars.nasa.gov/system/downloadable_items/40368_PIA22228.jpg
ブラウザに表示された画面のスクショです。
300x300にリサイズされた画像が表示されます。
ここでは、簡単な例を紹介しました。実際は署名を使ってセキュアにしたり、元画像のURLを難読化するなど設定して使うことになります。
詳しくは後述していきます。
URLパラメータ
imgproxyのURLパラメータについて説明していきます。
URLパラメータは、署名、処理オプション、画像ソースURL、拡張子(オプション)で構成されますが、処理対象の画像URL(以降、ソース画像URL)の形式によって若干異なります。
パターン1: ソース画像URLがそのまま
http://localhost:8080/{signature}/{processing_options}/plain/{source_url}@{extension}
-
{source_url}
にはソース画像URLがそのまま入る -
/{source_url}
の前に/plain
というパスが入る
パターン2: ソース画像URLをbase64でエンコード
http://localhost:8080/{signature}/{processing_options}/{encoded_source_url}.{extension}
-
{encoded_source_url}
には、Base64でエンコードされたソース画像URLが入る
パターン3: ソース画像URLを暗号化(PRO版)
http://localhost:8080/{signature}/{processing_options}/enc/{encrypted_source_url}.{extension}
-
{encrypted_source_url}
にはAES-CBCで暗号化されたソース画像URLが入る -
/{encrypted_source_url}
の前に/enc
というパスが入る
URLパラメータまとめ
以上が基本的なURLのパターンになります。
URLパラメータを表にまとめました。
パラメータ | 意味 | 参考リンク |
---|---|---|
signature |
署名 (署名しない場合insecure ) |
Signing a URL |
processing_options | 処理オプション (リサイズのパラメータなど) | Processing an image |
source_url | ソース画像URL | Source URL |
encoded_source_url | Base64エンコードしたソース画像URL | Base64 encoded |
encrypted_source_url | 暗号化したソース画像URL(PRO版) | Encrypted with AES-CBC |
extension | 拡張子(オプション) | Extension |
各パラメータの設定方法についても後述していきます。
Base64エンコードしたソース画像URL
タイトルそのままですが、
URLセーフなBase64形式にエンコードしたソース画像URLのことです。
Base64エンコードしたソース画像URLの生成方法
Pythonで実装してみました。
def generate_base64_source_url(source_url: str) -> str:
return base64.urlsafe_b64encode(source_url.encode()).decode()
if __name__ == "__main__":
source_url = source_url = "https://mars.nasa.gov/system/downloadable_items/40368_PIA22228.jpg"
print(generate_base64_source_url(source_url))
# aHR0cHM6Ly9tYXJzLm5hc2EuZ292L3N5c3RlbS9kb3dubG9hZGFibGVfaXRlbXMvNDAzNjhfUElBMjIyMjguanBn
処理オプション
対象の画像に適応する操作(リサイズ、クロップ、回転など)を指定します。
主な処理オプションを表に記載しました。
オプション | 説明 | 例 |
---|---|---|
resize (rs ) |
リサイズ方法1とサイズを指定 | rs:fill:300:300 |
crop (c ) |
クロップする領域を指定 | crop:1000:1000 |
rotate (rot ) |
回転する角度を指定(90度単位でサポート) | rotate:180 |
blur (bl ) |
ぼかしを指定 | blur:30 |
background (bg ) |
塗りつぶす背景色を指定(RGBまたはHEX) |
bg:255:153:0 , bg:ff9900
|
処理オプションの例
例えば、
500x500の範囲でクロップし、画像アスペクト比を維持せず300x100にリサイズするなら、処理オプションは下記の文字列になります。
insecure/crop:500:500/resize:force:300:100
以下は実際に処理をかけて表示された画像です。
微妙な画像が出来上がりました笑
これ以外にもさまざまな処理方法が存在します。
署名
画像の処理をセキュアに行うための「署名」について紹介します。
署名は画像URLが改竄されていないか確認するための暗号化されたトークンです。画像処理を外部からの不正なリクエストを防ぐことができるためセキュリティを強化できます。
署名の有効化
環境変数IMGPROXY_KEY
とIMGPROXY_SALT
を設定することで署名を有効化できます。
IMGPROXY_KEY
とIMGPROXY_SALT
は、
-
IMGPROXY_KEY
: 16進数でエンコードされたキー -
IMGPROXY_SALT
: 16進数でエンコードされたソルト
である必要があります。
下記コマンドを使用してキーとソルトを簡単に生成することができます。
echo $(xxd -g 2 -l 64 -p /dev/random | tr -d '\n')
上記で生成したキーとソルトをIMGPROXY_KEY
とIMGPROXY_SALT
に設定し、コンテナを再起動してください。
services:
imgproxy:
image: ghcr.io/imgproxy/imgproxy:latest
ports:
- "8080:8080"
environment:
- IMGPROXY_KEY=16進数でエンコードされたキー
- IMGPROXY_SALT=16進数でエンコードされたソルト
IMGPROXY_KEY
とIMGPROXY_SALT
は外部に公開されると署名の意味がなくなるので管理には注意が必要です。
署名URLの生成方法
署名付きURLの生成手順は下記の通りです。
- 署名より後のURLパスを定義しエンコード(エンコードURLパス)
- キーとソルト+エンコードURLパスを組み合わせてSHA256で暗号化されたHMAC署名を生成
- 生成されたHMAC署名をBase64エンコードしてURLセーフな形式に変換
- 上記の署名をURLパスのsigunatureに入れて署名付きURLが完成
といってもわかりづらいと思うので、公式のExampleを参考にPythonで署名付きURL生成関数を作成しました。
import base64
import hashlib
import hmac
def generate_base64_source_url(source_url: str) -> str:
return base64.urlsafe_b64encode(source_url.encode()).decode()
def generate_signature_url_path(key: str, salt: str, path: str) -> str:
key = bytes.fromhex(key)
salt = bytes.fromhex(salt)
path = path.encode()
digest = hmac.new(key, msg=salt+path, digestmod=hashlib.sha256).digest()
signature = base64.urlsafe_b64encode(digest).rstrip(b"=")
signature_url_path = b'/%s%s' % (signature, path)
return signature_url_path.decode()
if __name__ == "__main__":
key = "017fd0c61f868beb9b66d74b6a9f1275"
salt = "ab6d92e9e65e4cd804c502412acb9617"
process_option = "/rs:fit:300:300"
source_url = "https://mars.nasa.gov/system/downloadable_items/40368_PIA22228.jpg"
b64_source_url = generate_base64_source_url(source_url)
path = f"{process_option}/{b64_source_url}"
signature_url_path = generate_signature_url_path(key, salt, path)
print(signature_url_path)
# => /YVu2QQCmvaQFTGkZ_l_NF0bT1ri6JfpEYsaX8mzJq1E/rs:fit:300:300/aHR0cHM6Ly9tYXJzLm5hc2EuZ292L3N5c3RlbS9kb3dubG9hZGFibGVfaXRlbXMvNDAzNjhfUElBMjIyMjguanBn
生成されたURLパスにアクセスしてみるとリサイズされた画像が表示されます。
http://localhost:8080/YVu2QQCmvaQFTGkZ_l_NF0bT1ri6JfpEYsaX8mzJq1E/rs:fit:300:300/aHR0cHM6Ly9tYXJzLm5hc2EuZ292L3N5c3RlbS9kb3dubG9hZGFibGVfaXRlbXMvNDAzNjhfUElBMjIyMjguanBn
下記はブラウザに表示された画面のスクショです。
無事、リサイズされていることがわかります。
不正なリクエストを検証してみる
上記で生成されたURLパスのrs:fit:300:300
をrs:fit:100:100
に変更して画像処理部分を変更してリクエストしてみましょう。
http://localhost:8080/YVu2QQCmvaQFTGkZ_l_NF0bT1ri6JfpEYsaX8mzJq1E/rs:fit:100:100/aHR0cHM6Ly9tYXJzLm5hc2EuZ292L3N5c3RlbS9kb3dubG9hZGFibGVfaXRlbXMvNDAzNjhfUElBMjIyMjguanBn
すると403エラーForbiddenが返ってきます。
URLパラメータを改ざんできないことが確認できました。
おまけ
クラウドストレージ上の画像をimgproxyで処理する方法について紹介します。
弊社でよく使っているGoogle Cloud Storage(GCS) を対象とします。
まとめ
本記事では、imgproxyを使った画像処理について紹介しました。
imgproxyは高速で安全な画像プロキシサーバーです。豊富な画像処理オプションが存在し、署名によって不正なアクセスを防ぐ機能があります。
導入することで画像処理のロジックをWebアプリケーションから切り離すことができ、サービスの最適化にもつながってきます。
基本的な操作のみの説明になりましたが、imgproxyにはソース画像URLの制限やクラウドストレージとの連携、キャッシュの設定などWebアプリケーションで使用するために必要なさまざまな設定があります。
弊社では、特定GCSバケット内の画像に限定する設定を加え、CloudRun上に起動しています。
記事をみてimgproxyが気になった方はぜひ探ってみてください。
おわりに
ここまで読んでいただきありがとうございます!
引き続きアドベントカレンダー続きますのでよければ明日以降の記事もご覧ください!
弊社インティメート・マージャーに少しでも興味を持った方は下記もご覧ください!!