xcorp::When it rains, it pours.

"The nice thing about rain," said Eeyore, "is that it always stops. Eventually."

Slowloris

たまにはマジメなことも書かなくてはな。
近頃 ApacheSquid などなどに対する DoS 攻撃ツール slowloris が話題になってます。原理は、クライアントから中途半端なリクエストヘッダをサーバ側に大量に送信することで、HTTP セッションを大量に生成させつつ保持させてサーバ側のリソースを食いつぶすってな塩梅。このリクエストを Apache などがちゃんと処理できる状態になるまで溜め込んで待っているのが原因なんだから、ちゃんと処理できる状態になるのを待ってから Apache に渡すようにしてやればよろしいでござるの巻。
最初に試すのは TimeOut ディレクティブ*1ではなく、AcceptFilter。ただし、FreeBSD でしか効果がないのがタマにキズ。AcceptFilter http httpready を設定すると、accf_http(9) を利用し、カーネルレベルで HTTP リクエストをバッファリングして、完全なリクエストを受信してから Apache にデータを渡すようになる。しかしながら、slowloris もバカではないようで、サーバ側で httpready を利用してるとおぼしき場合のためにこれを回避するオプション*2があり、これを使うと FreeBSD でもまともに食らってしまう。tcpdump でデータを見てみたら GET ではなくて POST でリクエストしていた。調べてみたら accf_http の方が GET と HEAD にしか対応しておらず、POST には対応してなかった(;´Д`)
Linux ではどうかというと、前述の AcceptFilter の項に書いてあるとおり、LinuxTCP_DEFER_ACCEPT は HTTP リクエストのバッファリングをサポートしてないので、無条件に食らって終了。
そんな中、mod-pacify-slowloris*3 という Slowloris 対抗 Apache モジュールが出てました。以下の 3 種類の方法で防御するということらしい。

PacifyLorisMaxConnectionPerIp
1 IP あたりの最大コネクション数 (デフォルト値: 50)
PacifyLorisMaxHeaderPerRequest
1 HTTP リクエストあたりの最大ヘッダ数 (デフォルト値: 20)
PacifyLorisMinHeaderPerSec
1 秒あたりの最小ヘッダ数 (デフォルト値: 100)

素の slowloris であれば効果はテキメンできっちりガード。あらステキ。しかし、素のままで使う方がむしろレアなこのご時世、PacifyLorisMaxHeaderPerRequest と PacifyLorisMinHeaderPerSec に関しては、ちょいと slowloris を改造すると終了*4。残る頼みの綱の PacifyLorisMaxConnectionPerIp も DDoS とか botnet に実装されてしまったら効果は半減。うむむ。
そもそもこれは、完結しないリクエストをずっと待ち続けるという本質的な問題をクリアしなければいけないんだが、mod-pacify-slowloris はこの問題を修正するモジュールではないので、今のところどうしようもないというのが私の個人的な見解。てか、この攻撃をモノともしない IIS とか lighttpd とかってどうやってんのかね?どういう仕組みになってるんだろう。

*1:こんなの意味ないし

*2:-httpready オプション

*3:id:tessy さんのお友達、zou 団の kikuzou さん作

*4:この件に関しては、私の隣に座ってるダンディーな人がすでにレポートを送っているので、何らかの対処が行われると思います