CVE-2012-どさにっき 〜2012年3月上旬〜

by やまや
<< = >>

2012年3月1日(木)

Apache 2.4 BufferedLogs

_ 2.4 のドキュメントを読んでいたら、 BefferedLogsで実験的という文字が消えていることに気がつく。おおおおぉぉぉ。これでおおっぴらに使える。2.2 までは experimental 扱いだった。

_ 最近はハードウェアが無駄に高スペック化していて、いまや CPU は複数コアでないのを探す方が困難で、メモリも HDD も足りなくて困るということはまずない。大して処理能力の必要がないサーバではリソースが余りまくってしまう。そんなわけで1台のハードウェアに複数の仮想ホストを収容することが多い。

_ 罠。数字の上では潤沢にリソースがあるように見えても、分割すると意外と性能が出ないことがある。その筆頭がディスク I/O。HDD の容量は昔よりずっと増えてるけど、データを読み書きする磁気ヘッドの数はほとんど変わっていない。同じ母艦に同居する複数の仮想マシンが同じディスクを読み書きするわけで、なのにヘッドの数が増えてないということは仮想マシン1台あたりのディスクのヘッドはむしろ少なくなっているということ。あっちの仮想マシンが何か書き込みしている間はこっちの仮想マシンは待たされる、という現象が頻繁に起きる。

_ で、仮想化したとある Web サーバで見事にこれを踏んでしまった。CPU やメモリには余裕があるんだけど、アクセスログの書き出しが追いつかなくてピーク時には load average が3桁とかとんでもない値に。

_ そんなときに有効なのが BufferedLogs というディレクティブ。応答を返すたびにログを書くのではなく、ある程度数を溜めこんでからまとめて書くようになるので I/O が改善する。しかし改善することがわかっていても、2.2 では実験的という扱いなので、大量のアクセスがある実環境でこれを使うのはちょっと勇気がいる。とはいえ load avg 3桁というのはさすがにありえないのでやむなくつっこんだら、5以下まで劇的に下がった。

_ それが、2.4 ではありがたいことに experimental という文字が消えた。めでたい。ま、たぶん中身は変わってなくてドキュメントの記述が変わっただけなんだろうけどね(確認はしていない)。

_ なお、パフォーマンスの改善はあるけど、溜めこんだログが書かれてないうちに httpd が死んだりホストが死んだりすると、ログは虚空に消えて復活できなくなるという危険もある。I/O 性能でとくに困ってないホストでまでこれを有効にするのはやめておいた方がいい。


2012年3月2日(金)

Apache 2.4 mod_data

_ mod_data。コンテンツを RFC2397の data URL scheme、たとえば data:text/plain,Hello,%20Worldみたいな URL に変換するフィルタモジュールなんだけど、謎。どうやって使うんだ?

_ 小さいアイコン画像なんかは、HTML の中に埋めこんでしまえるならそうした方が HTTP のセッションの数が少なくなって効率的で、そのために data: は有効なんだけど、mod_data はそれを支援するためのモジュールではなさそう。これまで HTML に <img src="/path/to/icon.gif"> と書いていたものを <img src="data:image/gif,..."> と書き換えなくちゃいけないんだけれど、それをやってくれるわけではない。HTML を書き換えるのは人間の仕事。それをやってくれないなら、このモジュールにいったい何の価値が?

_ どういう URL に書き換えればいいのかを知るのにこのモジュールが使えないこともない。

-- data-url.html
<html>
<body>
<form method="POST">
<input type="file"><input type="submit">
</form>
</body>
</html>

-- .htaccess
<Files data-url.html>
    <If "%{REQUEST_METHOD} == 'POST'">
	SetHandler reflector	← 入力されたものをそのまま返す
	SetOutputFilter DATA	← 出力を mod_data で URL 形式に変換する
    </If>
</Files>
こんな HTML と .htaccess を用意しておくと、フォームからアップロードしたファイルを data: な URL に変換してくれる。要 mod_reflector(これも 2.4 からの新モジュール)。こういうことをやるのに1行もプログラムを書かなくてもいいというのは便利ではあるけれど、しかしそんなのは Apache でやるよりもコマンドラインのスクリプトでやった方がずっとよさげな。ちっとも難しいスクリプトじゃないんだし。
#!/bin/sh
filename="$1"
echo -n "data:image/${filename##*.};base64,"
openssl base64 < "$filename" | tr -d \\n

_ HTML に含まれる <img src="..."> を data: に自動で書き換えることを考える。んーと、<img> タグを mod_sed(これまた 2.4 の新モジュール)で <!--#include ... --> な SSI に変換して、さらにこの SSI を処理して HTML の中に mod_data で変換した data: な URL 文字列を include すればいけるかな。こんな感じ。

<Location /image/>
    SetOutputFilter DATA	← mod_data で URL に変換
</Location>

<Files hoge.html>
    SetOutputFilter SED;INCLUDES	← HTML 書き換え + SSI
    OutputSed "s%<img src=\(\"/image/[^\"]*\"\)%<img src=\"<!--#include virtual=\1-->\"%g"
</Files>
試してみると、えーと、失敗。SSI の <!--#include virtual=...> って CGI もちゃんと実行して取りこんでくれるくせにフィルタはかからないのか。そんな中途半端な実装だとは知らなんだ。なので、data: に変換されずにバイナリな画像の中身がそのまま <img> タグの中で展開されてヒドいことになった。まあ、考えてみたら deflate フィルタで gzip 圧縮されたものがそのまま include されても困るし、これが妥当な挙動か。

_ ってことで、いろいろ考えてみたんだけど有用な使い方は思い浮かばず。data: 自体が有用であることは間違いないんだけど、mod_data っていったい何なの?


2012年3月7日(水)

Apache 2.4 DefaultType

_ 2.4 で動作が変わったディレクティブってのもいくつかあるけど、地味にインパクトが大きいのはこの DefaultTypeじゃなかろうか。設定してると起動時に警告が出る。

_ DefaultType は、ファイルの拡張子やら CGI の出力やらその他もろもろの手段によっても mime type が決定されなかった場合に付与されるタイプを設定するもの。…だった。2.4 では、仕様が変わって DefaultType None が唯一可能な記述になり、つまり、「なにもしない」以外の選択肢はない。2.2 まででは DefaultType があれば Content-Type が空の応答を返すことはなかったが、2.4 では DefaultType が機能しなくなったので Content-Type なしの応答もありうる。

the server will send the response without a Content-Type header field and the recipient may attempt to guess the media type.
え、応答を受けとった側が推測するの? まじで?

_ ドキュメントを読むと AddType や ForceType で逐一設定しろ、とあるんだけど、こんなの設定してられない環境もあるだろうし、DefaultType で救済してくれるのはたいへんありがたかったんだが。具体的にはですね、以前うちで使ってた無線ルータ(だいぶ前に壊れた)の Web UI が Content-Type を返さないクソ仕様で、IE 以外のブラウザでは HTML のソース丸見えで操作できなかった。推測できてないじゃん。ダメじゃん。そゆことで、その腐れルータを使ってた当時は DefaultType text/html な設定のリバースプロクシをかませることで Content-Type のない応答を書き換えて正しく画面を表示できるようにしていた。これを DefaultType を使わないようにしようとしても、AddType はプロクシ経由のコンテンツにはたしか効かなかったはずだし、ForceType は正しく image/gif が返ってきてる画像アイコンまで text/html に書き換えちゃうし、で使えない。DefaultType が唯一正しい選択肢だったんだけど、その方法が 2.4 では封じられてしまった。非推奨で初期値は何もしない、でもかまわないから前のまま残しておいてほしかったんだが。

_ いちおう、httpd.conf に以下のように書くと、以前の DefaultType と同様の動作になる。要 mod_lua。

<LuaHookFixups default_type>
    function default_type(r)
        r.content_type = r.content_type or "text/plain"
    end
</LuaHookFixups>
プロクシ越しでも効くかどうかは確認してないけど、理屈からすれば動くと思う。

_ 試してないけど、もしかしたらこんなのでもいけるかも。

Header set Content-Type text/plain "expr=-z %{CONTENT_TYPE}"
Header ディレクティブ自体は以前からあるけど、expr=... ってのは 2.4 以降の新機能なんで、2.2 ではエラーになる。


2012年3月8日(木)

Apache 2.4 AllowOverrideList

_ Apache 「.htaccessファイル」で Web 改ざん、Web サイト管理者は確認を。怪しげなサイトにリダイレクトする .htaccess を仕込まれるらしい。Redirect か RewriteRule を使ってるんだろうから、httpd.conf で AllowOverride で FileInfo を許可していなければこの攻撃は防げる。

_ が、FileInfo を禁止すると、AddType やら AddHandler やら SetEnvIf やらも .htaccess で使えなくなってしまう。Apache の設定ディレクティブは広範にわたっているのに、分類がおおざっぱすぎるんだよな。.htaccess で Redirect は使わせたくないけど、AddType は許可したい、ってのができない。

_ 2.2 までは。

_ 2.4 では AllowOverrideList ってのが新設されて、ディレクティブ単位で .htaccess での利用可否を決めることができるようになった。

AllowOverride AuthConfig
AllowOverrideList AddType AddHandler
こう設定しておくと、認証関連と AddType、AddHandler は .htaccess に書けるが、Redirect は書けない。なんでできないんだと前からずっと思ってたことをやっと実現してくれた。サーバを設定する人とコンテンツを作る人が別になってるようなことってけっこうあるけど、ある程度自由にいじれるように .htaccess でいじれる範囲を増やしておくと、知らない間に腐った RewriteRule を仕込まれて Internal Server Error 出まくりなんて事故がたまに起きる。AllowOverrideList を使えば、危険そうなものは禁止しておいて、どうしても使いたければ .htaccess ではなく httpd.conf で設定するから相談してくれ、ということにしておけばいい。ほんとになんで今までこれができなかったんだろう。

_ もっとも、2.4 を検証用途ではなく実際の現場で使うようになるのはまだずっと先だろうから、当分の間は 2.4 なら制限できるのに、とぼやき続けるんだろうけどな。つーか、mod_rewrite はなんであんなアクロバチックなことをしてまで .htaccess で使えるようにしたんだよ。あれ書いたやつアホだろ。あんなの .htaccess で使える必要ないのに。

_ そいえば、 2.4 では Allow/Deny でやってたことが Require に統合されたけど、AllowOverride 的には Allow/Deny は Limit で Require は AuthConfig だから .htaccess での利用制限に影響があるんだな。つまり AllowOverride AuthConfig な設定ではこれまで .htaccess でユーザ認証は設定できたけどアクセス制限は設定できなかったけど、2.4 ではどっちもできてしまう。どちらも Require を使う以上 AllowOverrideList でも制限できないし、これどうするんだろう。ま、Limit を禁止して AuthConfig だけ許可することなんてことはまずめったにないだろうからあんまり関係ないか。


<< = >>
やまや