水無月ばけらのえび日記

bakera.jp > 水無月ばけらのえび日記 > RangeつきリクエストによるApacheのDoSとApache Killerの実力

RangeつきリクエストによるApacheのDoSとApache Killerの実力

2011年8月27日(土曜日)

RangeつきリクエストによるApacheのDoSとApache Killerの実力

公開: 2011年8月30日1時45分頃

ApacheのDoSの脆弱性が話題になっていますね。

HTTP/1.1では、HTTP要求ヘッダでRangeフィールドを指定すると、コンテンツの全てではなく一部分だけを要求することができます。たとえば、以下のように指定するとデータの先頭の1バイトだけを受け取ることが期待されます。

Range: bytes=0-0

※これは以前にも書いたのですが、0-0で1バイト受け取るというのは微妙に直感的ではない感じがしますね。しかし正しい挙動です。

WebサーバがRangeを解釈した場合、ステータスコード 206 (Partial Content) で応答しつつ、指定された部分だけを返します。

と、これだけならRangeがない場合とサーバの負荷はほとんど変わらないのですが、実は1回のリクエストで複数の範囲を指定することができます。その場合、応答は Content-Type: multipart/byteranges となって、1回の応答で全ての範囲が返ります。RFC2616の19.2には以下のような例が出ています。

HTTP/1.1 206 Partial Content

Date: Wed, 15 Nov 1995 06:25:24 GMT

Last-Modified: Wed, 15 Nov 1995 04:58:08 GMT

Content-type: multipart/byteranges; boundary=THIS_STRING_SEPARATES

--THIS_STRING_SEPARATES

Content-type: application/pdf

Content-range: bytes 500-999/8000

...the first range...

--THIS_STRING_SEPARATES

Content-type: application/pdf

Content-range: bytes 7000-7999/8000

...the second range

--THIS_STRING_SEPARATES--

以上、RFC2616 19.2 Internet Media Type multipart/byteranges より

指定される範囲の数が増えると、パートの数が増えていくわけです。

さらに興味深いことに、範囲は重なっていても良いことになっています。RFC2616の14.35.1には以下のような例が出ています。

- Several legal but not canonical specifications of the second 500

bytes (byte offsets 500-999, inclusive):

bytes=500-600,601-999

bytes=500-700,601-999

以上、RFC2616 14.35.1 Byte Ranges より

bytes=500-700,601-999 という指定が可能だという点に注目してください。これはRFCにはっきり例示されている、れっきとした仕様です。

ただし、引用した例の前に "not canonical" と書いてあることにも注意が必要です。bytes=500-700,601-999 のような指定は冗長な書き方で、bytes=500-999 を指定したのと同様だと解釈されることが期待されます。

しかしApacheの実装ではそうなっていないようで、このような冗長な指定をまとめずに個々に処理してしまうようです。それを利用したのが今回の問題で、攻撃ツール「Apache Killer」は以下のようなRangeを送ってきます。

Range: bytes=0-,5-0,5-1,5-2,5-3,5-4,5-5,5-6,5-7,5-8,5-9,

(~中略~)

5-1293,5-1294,5-1295,5-1296,5-1297,5-1298,5-1299

これも本来であれば一つにまとめられて Range: bytes=0- と解釈されるべきですが、Apacheは律儀に大量のパートを生成しようとして、大量のメモリを消費してしまいます。

ところでこの攻撃、威力があるという話とそうでもないという話が両方出ているようです。良く分からないなあと思っていましたが、徳丸さんがその原因を調査されていました。

結論を簡単にまとめると、以下のようになります。

このため、テストのために暫定的に作ったような環境では、効果が薄くなる場合があります。逆に、きちんとコンテンツが置かれ、しっかりパフォーマンスチューニングされた環境は攻撃に弱いということになります。つまり、テスト環境は攻撃に強いが本番環境は攻撃に弱いということになりがちです。

ということで、油断しないで対応を考えた方が良さそうですね。

根本的なApacheの修正としては、RFC2616で期待されているように、冗長な指定は正規化して複数区間をできるだけまとめるという対応が望ましいでしょう。それでも歯抜けの細切れを指定されればバウンダリの分だけレスポンスのサイズは増えますが、組み合わせ爆発的な増加は避けることができるはずです。

暫定的な対応としては、Rangeを無視してしまうという方法も考えられます。RFC2616 14.35.2 には以下のようにあり、

A server MAY ignore the Range header.

以上、RFC2616 14.35.2 Range Retrieval Requests より

WebサーバはRangeを無視する可能性があるということになっていますので、仕様的には問題ありません (ダウンロード中断・再開ができなくなって残念な思いをする人が出る可能性はありますが)。

関連する話題: Web / セキュリティ / Apache / HTTP

最近の日記

関わった本など