本記事はImageMagick関連の記事の2本目です。ImageMagickの既知の脆弱性、システム情報の漏洩などの問題を扱った1つ目の記事はこちらです。2回目の今回は、画像のアップロード機能を利用したDoS攻撃を取り上げます。前提とする環境などは前回と同じです。PNGの3種類に絞っているとします。記事中では右の略語を使っています。CarrierWaveDoS画像のアップロードに関連するDoSにはいくつかの種類があります。画像処理ツールが行う画像処理による負荷を利用する1,2についてはIMとの関連が薄く、3は基本的にIMのアップデートにより解決すべき問題であるため、今回は4の画像処理による負荷を利用したDoSのみを取り上げます。画像処理はそれなりのコンピュータ資源を消費する重い処理です。画像によるDoSは、これを悪用して、細工した画像によりサーバに大きな負荷をかけさせる攻撃です(この種の攻撃に言及する日本語の文献は少ないのですが、徳丸氏の書籍には関連する記述があります)。具体的な攻撃方法を含む情報としては、dutchgraa氏が2013年にHackerOneに出した3つのバグレポートがあります。compressionいずれもメジャーな画像形式の、かつ比較的小さいファイルサイズの画像を使った攻撃です。flood攻撃について説明します。64,250に書き換えただけの、5KB弱のJPEGです。この画像をIMでサムネイルの作成などを行っているサイトにアップロードすると、サーバに大きな負荷がかかり応答の遅延が生じます。このときIMの内部では、画像の各ピクセルの色データを保持するためのピクセルキャッシュと呼ばれる領域がメモリに確保され、これを埋めていく処理が実行されます。メモリに収まらない場合、ピクセルキャッシュはストレージ上に一時ファイルとして保持されます(下図)。i7-8700)でこのJPEGのサムネイル作成に要した時間は、SSDのストレージにピクセルキャッシュが置かれる場合は10分弱、オンメモリの場合は1-2分でした。同じJPEGを複数アップロードしたところ、I/O、メモリ使用量、CPUのI/O待ち、ブロックされているプロセス数、スワップ量などが増加し、最後にはリブート以外には何もできない状態に陥りました。サーバのスペックにもよりますが、DoS画像がかなりの威力を持つことが分かります。氏の他の2つのレポートについても触れておきます。floodingのPoCは数万のフレームを持つアニメーションGIFです。compressionは、圧縮されたメタデータを格納するチャンクに、解凍後に非常に大きくなるデータを入れたPNGです。いずれもIMで処理すると、大量のメモリを消費して、応答が遅延する現象が発生します。IMの環境を実際に作って試してみればわかりますが、DoS画像が与える負荷は相当大きく、サーバのスペックによっては、単一の画像による攻撃でもサーバが使用不能になってしまいます。それはどのような環境でも同じように起こる訳ではなく、環境を構成するソフトウェアに依るところが大きいです。IM)では、画像のピクセル数、アップロードファイルのサイズ、メモリ使用量、実行時間などの上限がデフォルトで無いか、あっても大きな値になっており、これがDoSへの耐性を低くしています。バグによりOSがIMをkillできなくなるからです。このバグは補正値(/proc/$PID/oom_score_adj)を書き換えることで回避できます。上で説明した攻撃の対策を見ていきます。基本編と応用編に分けています。前回の記事と同じく、IM7とMiniMagickの使用を想定しています。基本編1前記事で画像形式を絞る方法としてIMのポリシーファイルを取り上げましたが、ポリシーにはDoS対策を意図したと思われる設定項目もあります。画像のピクセルサイズや、個々の画像の処理ごとにピクセルキャッシュで消費される、ストレージ量、メモリ量、時間などのリソース量を制限できます。下記は、縦/横がともに2KPまでの画像をオンメモリ・単一スレッドで処理することを念頭に、筆者が検証環境のIM7に設定したリソース制限の例です。2)やpolicy.xmlのコメントにありますが、クックパッドのブログが詳細で分かりやすいです。ちなみに、オンメモリ・単一スレッドにしたのは、単一の画像処理が与える負荷の量を分かりやすくするためです。CW側でできる設定もあります。mogrifyコマンドを実行して画像処理を行います)、ファイルサイズにも上限を設けましょう。endこれらの設定を行いJPEGやGIFのDoS画像をアップロードすると、期待どおり即座にエラーになります。基本編2ここから基本編2です。2を作ったのは、1の対策には大まかに言って2つの点で穴があるからです。1つ目の穴は、IMのポリシーの仕様に起因します。ポリシーで制限できるのはピクセルキャッシュにかかわるリソースのみであるため、一部のDoS画像において、実際のメモリ使用量や処理時間がポリシーの設定値を大きく超える現象が発生します。これを考慮してMiniMagickのタイムアウトも設定しましたが、メモリ量についてはカバーできていません。2つ目は、基本編1の設定で制限しているのは単発のDoS画像による負荷だけだということです。何らかの制限を加えても画像処理のリソース消費はかなり多いため、(同時接続数の設定によっては)複数同時の攻撃によりサーバのリソースを使いつくしてしまう可能性があります。次の応用編で書くように、これらの穴を埋める方策は色々とありますが、ここでは画像のDoSにより簡単にサーバが落ちないよう、サーバ全体でIMに大きな枠をはめることにします。そのツールとして使えるのはcgroupです。cgroupは、任意に定義するグループの単位でリソース消費を制限するLinuxの仕組みです。全てのIMのプロセスを同じグループに入れてやれば、それらが使う合計のリソース量に上限を設定できます。下記は筆者が検証環境に作ったグループ設定です。全部の画像処理で、物理メモリは1GiB、CPUは2個(コア)まで。}このグループの制限をcgrulesやcgexecを使ってIMに適用してやれば、単一もしくは複数のDoS画像がサーバ全体のCPUやメモリを使いつくすことはなくなります。画像処理を別のマシンで実行することでも、IMによりWebサーバが過負荷に陥ることを避けられます。そのマシンをNW的に隔離すれば、IMの脆弱性の影響を封じ込めることもできます。これまでの対策で何のリソースがどこまで制限できているか、さらに取りうる手は何なのかを整理したいと思います。メモリ全体での「枠」をcgroupではめています。少数の画像がこの枠を使い切ることを防ぐには、階層化したcgroupなどを使って、個々の画像処理に対するリソース制限を厳密にします。・ストレージ(I/Oと使用量)既に画像の形式やサイズを絞っており、またオンメモリ処理にしたため、ほぼ対策できています。画像処理の同時実行数を絞って、Webサーバの同時接続数より少なくすれば、時間が掛かる画像処理が接続数を使いつくすことも防げます。上記を含めて、さらなる対策の候補となりうるものを下記にまとめます。同時実行数の制限と、階層化したcgroupの例は筆者個人のGistにあります。サーバ全体の画像処理のリソースや処理の同時実行数だけを絞ると、画像アップロード機能だけを使用不能にするのはむしろ容易になる場合があります。この種の設定の目的は、画像処理の負荷によって他の機能やサーバ全体が使用不能になることを防ぐことです。まとめ今回は画像処理の負荷を利用するDoSについて書きました。画像処理はリソースを多く消費するため、アプリケーション層でのボトルネックになりえます。Passengerの環境は、デフォルト状態では画像処理に関連するリソースの制限をほぼ欠いており、DoSの影響をうけやすいと言えます。対策は基本編と応用編に分けました。基本編に記述したのは、1IMのポリシーなどの設定と、2cgroupによる、画像のフィルタとリソース消費量の制限です。これらを含め、複数の対策候補を基本編~応用編に記述しましたが、全てがMUSTだと言っているわけではありません。そもそも完全なDoS対策はありませんし、可用性の要求レベルはサイトにより異なります。IMは突出したボトルネックになりうるものであり、何らかの手当てをするのが望ましいと思いますが、どこまでやるかはサイト全体のDoS対策を検討する中で判断してほしい、という趣旨で書いています。NW層のDDoSなどと違って、画像DoSの攻撃対象はWebアプリの一部の機能に限られており、また現状で攻撃が多発していることを示す情報も、筆者が知る限りありません。サイトの可用性の要求レベルが低く、かつ画像アップロード機能の重要性が低いのであれば、基本編の対策のみをしておく、または攻撃を受けた際に該当機能だけを一時的に無効化して凌ぐ、などの対処もあるでしょう。応用編に挙げたように、画像データのチェックを強化するという真正面からの対策もあります。画像の解析器を自前で作る必要があり、実装のハードルはやや高いですが、既知の種類のDoS画像を処理の早い段階ではじくのに有用ですし、ユーザフレンドリーなエラーメッセージを出すことも簡単です。