ImageMagick

「さようなら ImageMagick」の考察

はじめに

サイボウズさんの ImageMagick の利用をやめる記事について少し思う所を書きます。否定でなくアシストのつもりです。

「ImageMagick を外した理由」

脆弱性

ImageMagick には脆弱性が大量に存在します。
2017 年に報告された ImageMagick の脆弱性は 236 件 でした。
大量にある上にリモートコード実行級の脆弱性もあり、
安全性という観点ではかなり厳しい評価をしなければなりません。

脆弱性は数だけでなく、その中身も問題です。

スクリーンショット 2018-08-26 2.03.55.png

ざっとリストを見ると、ReadDIBImage、RWriteBMPImage といったファイル形式の読み書きの不具合が多い事に気づくでしょう。

%  ls git/ImageMagick/ImageMagick/coders/*.c | wc
     128     128    6880

ImageMagick は 100 種類以上の膨大なファイル形式に対応していて、古式ゆかしい Sun 形式から最近では WebP, HEIC(HEIF) 形式まで扱えます。読み書き出来るファイル形式の種類の数だけ脆弱性が多く出やすいのです。

これらの状況は、ImageMagick をサーバで動かしユーザから任意の画像ファイル形式を受け取って処理するのは自殺行為である事を示唆します。受け取る画像ファイル形式を最小限に絞るべきです。

  • policy.xml で JPEG/GIF/PNG のみ READ|WRITE 処理を許可する設定にする
  • 画像ファイルのバイナリ先頭を見て JPEG/GIF/PNG のシグネチャと一致しない場合はエラーにする
  • 脆弱性をつかれても被害をある程度限定できる VM 環境で動かす

そこそこの規模のサイトで ImageMagick を使う場合、これらの組み合わせで対応をしているでしょう。(やってなかったら、今すぐして下さい
!)

尚、policy.xml での設定は以前はかなり面倒でしたが、6.9.7-7 から楽になりました。

是非、ご活用を。

とはいえ、ファイル形式さえ絞れば安全だ!等と言うつもりはありません。
そんな自信がなくなるような近年の ImageMagick脆弱性クリティカルヒットを2つ紹介します。

クリティカルヒット(1) ImageTragick (CVE-2016–3714)

ImageMagick が対応する画像ファイル形式の中にコマンドを含められるものがあって、何でも実行出来たという恐ろしいものです。
これは policy.xml なりで制限できます。これを機にファイル形式を絞りはじめたサイトも多かったのでは無いでしょうか。

クリティカルヒット(2) Yahoobleed (CVE-2017-9098)

自分が特に痛いと思うのはこちらです。。メモリ初期化が不十分で前に処理した画像ファイルを読み出せる不具合。
Yahoo メールで他の人の画像が読めてしまい、Yahoo さんはこれで ImageMagick から離れました。

当然修正はされていますが、そんな不具合が入るソフトウェアが信用できるかという話になりますよね。。

コード実行脆弱性

コードを実行できる脆弱性は、こちらに一覧があります。

多くは、policy.xml でファイル形式を限定すれば避けられます、2016年の profile.c が痛い。。

OSS-Fuzz の活動

2017年の脆弱性の多さには理由がありまして、OSS-Fuzz という優秀な脆弱性検査ツールで 2017年から徹底的に調べられているのが、大きな要因です。

OSS-Fuzz については、こちらの記事をどうぞ.

以前の ImageMagick は脆弱性が多かったのは確かですが、今はそれらが改善して(以前に比べて)かなり安全になったとも言えます。

「PNG の変換画像が僅かに暗くなるケースがある」

disintegration/imaging のリサイズ処理

暗くなるかはともかく画像に差異が出る要因として以下のものが思いつきます。

  • 内部処理でのビット深度
    • Imaging の内部表現は Uint8 (= RGB24ビット)
    • ImageMagick の内部表現は(デフォルトで) Q16(= RGB48ビット)。
  • リサイズのアルゴリズムは?
    • Imaging のサンプルが Lanczos3 なので恐らく、これを使ってる?
    • ImageMagick の縮小デフォルトも Lanczos3 。でも、こっちは多項式近似してる。
  • リサイズの縦横の扱い (実はこれで微妙な差異が出る)
    • Imaging は横を縮めてから縦を縮める
    • ImageMagick は縦横同時に縮める

むしろ、これらの小さな違いよりメタデータの処理の方を気にしたほうが良いでしょう。
ImageMagick はガンマ値やICCプロファイル等の補正をしますが、Imaging だと恐らく自力で頑張る必要があるかと。あと CMYK は RGB に変換してあげなきゃとか、ちょっと手間かかりますね。

投稿して色が変な時にユーザが頑張って変換して投稿し直して頂く。といったサービスレベルも有りだと思います。

「変換できない画像がある」

BMPv4, v5 の対応

BMP 対応を謳うのであれば、折角ですし Go に v4,v5 対応をコントリビュートしてあげると良いかも。

image.png

v4 から RGBmask、v5 から ICC プロファイル対応なので、ちょっと大変そうです。

error concealment

ImageMagick は壊れた画像も変換できる という謎機能

これは謎という訳でもなく、信号処理での一般的な手法です。

ブラウザもある程度頑張って表示しますし、単純にデータの整合性が崩れる場所をデフォルト値扱いするといったレベルの対応が多いので、それほど脆弱性に寄与するとは思えません。勿論、処理を減らした方が脆弱性は減るので、そこはバランスの取りようです。

最後に

自分も ImageMagick を使わない方が良いと判断した仕事もいくつも持っていますが、脆弱性を理由にする事はあまりなくて、

  • メモリを大量に使う。そして重たい。
  • 画像をストリーム的に使えない。オンザフライ出来ない。
  • 扱えない画像形式もある。HEIC は最近まで扱えなかったし。
  • 画像認識の機能が少ない。OpenCV を使おう。
  • 機械学習系の処理がない。PIL や scipy を使おう。

といった具合です。自分はリリースの度に全差分見ているので、怪しいと思ったら更新止めたりしてます。

あと、C言語で実装すると脆弱性が入りやすいのは実感していて、Golang なり Rust なりで実装した ImageMagick に代わる決定的なソフトが出てこないかなといった期待はしてます。自分もサブセット的なのは仕事で作りますが、汎用的なのを作る機会はないですね。うーん。(ぶっちゃけ画像ファイルを安全に読み書きするのだけでも凄い大変なんです。。)