画像投稿機能を実装したシステムを構築する際に、注意する点として挙げられるのがEXIF情報です。
EXIF情報には画像に関する情報がメタデータとして含まれています。
その情報の中には緯度経度が含まれていることがあります。
これらの情報を適切に操作しなければ、ユーザーをストーカー被害に遭わせてしまう危険性があります。
そのEXIF情報をPHPの拡張モジュールであるImagickを使用して削除します。
またImagickのその他の使用方法についても解説します。
インストール方法
ImageMagickのインストール
sudo apt-get install -y imagemagick
Imagickのインストール
sudo pecl install imagick
php.iniの編集
# php.iniのパスを表示
php --ini
# viエディタでphp.iniを編集
sudo vi /path/path/path/path/php.ini
# 省略
# 以下の文を追加
extension=imagick.so
これでインストールは終了です。
Webサーバーの再起動を行えば準備完了です。
使用方法
Imagickインスタンスに画像ファイルの引き渡し
ImagickのreadImage()
メソッドではUploadedFile
型の引数を受け取ります。
つまりは、Bladeから送られてきた画像ファイルを$request->file()
メソッドで取り出し、そのままインスタンスにセットすることが可能です。
<form action="{{ route('example.store') }}" method="POST" enctype="multipart/form-data">
<input type="file" name="image" accept="image/*">
</form>
上記のようなBladeで画像ファイルをPOSTする際、バックエンド側では以下のようなコードでImagickインスタンスに画像ファイルをセットできます。
use Imagick;
public function store(Request $request)
{
$imagick = new Imagick();
$imagick->readImage($request->file('image'));
}
画像ファイルを受け取る場合、適切なバリデーションを行なってください。
データ型、MIMEタイプ、最大容量等を限定し、悪意のあるリクエストへの対策をしましょう。
EXIF情報の存在確認および削除
getImageProperties('exif:*')
ではその画像ファイルのEXIF情報を取得しています。
stripImage()
では画像ファイルのEXIF情報を削除します。
基本的に多くのサービスではEXIFが適切に削除されており、EXIF情報が存在していない画像もある為、その場合はスルーします。
# 先ほどの処理に続く
if (!is_null($imagick->getImageProperties('exif:*'))) {
$imagick->stripImage();
}
この処理で位置情報を削除でき、安全に画像サービスを構築できます。
これ以下は別の用途での使用方法です。
MIMEタイプ、 画像フォーマットの取得
元画像をアーカイブとしてストレージに保存し、DBに情報を残す場合など有効活用できそうです。
$imagick->getImageMimetype(); #JPEGの場合、戻り値は'image/jpeg'
$imagick->getImageFormat(); #JPEGの場合、戻り値は'JPEG'
画像フォーマットの変換
JPEGの1/5ほどの容量に圧縮されるものの画質低下が起こらないWebPという画像フォーマットに変換します。
WebPにすることで容量が軽くなり、画像表示の際の読み込みが高速化します。SEO対策にも良いみたいです。
$imagick->setImageFormat('webp');
引数には各画像フォーマット名を入れることで別のフォーマットにも変換できます。
バイナリーデータ化
getImagesBlob()
メソッドでは画像ファイルをバイナリーデータ化できます。
$imagick->getImagesBlob();
「あ〜DBにBLOB型で画像入れちゃう子ね」ってなったかもしれませんが、用途は別です。
バイナリーデータ化する事で、S3などのストレージにアップロードが可能になります。
$path = 'image/fruits/apple.webp';
Storage::disk('s3')->put($path, $imagick->getImagesBlob());
この処理でEXIF削除後の画像ファイルをストレージに保存できるようになります。
S3であればimage/fruits
prefixにapple.webpという名前で画像が保存されます。
Imagickインスタンスにバイナリーデータの引き渡し
$imagick = new Imagick();
$imagick->readImageBlob($blob); # $blobはバイナリデータの文字列です
この処理とすでに紹介した処理を合わせることで、DBに保存されているBLOB型のデータを画像ファイルとして復元し、EXIF削除やフォーマット変換を含めたストレージへの移行バッチ処理などに応用できます。
リサイズ
resizeImage()
$width = 200; # px単位
$height = 200; # px単位
$imagick->resizeImage($width, $height, Imagick::FILTER_LANCZOS, 1);
第3引数はリサイズ時のフィルターになりImagick::FILTER_LANCZOS
を使用することで高品質のままリサイズが可能になります。
第4引数ではBlurを指定し、1より大きいとぼかしが強くなり、1より小さいとシャープな画像になります。
thumbnailImage()
サムネイルなどを作成する場合はthumbnailImage()
を使用します。resizeImage()
はthumbnailImage()
に比べメモリ使用量が多いようです。
またメタデータもついでに削除し、最適化されたリサイズを行なってくれます。
$width = 200; # px単位
$height = 200; # px単位
$imagick->thumbnailImage($width, $height, true);
参考:Imagickドキュメント
最後に
ImageMagickは画像処理ツールとして最も広く使用されているもの1つである一方、過去にImageTragick等の深刻な脆弱性が発見されたこともあります。
強力なツールではあるものの、セキュリティ対策を怠ることはリスクに繋がる為、最新バージョンへのアップデートや使用用途を絞ったり、代替ソフトで対応するなどといったことも必要になる場合があります。