Apacheのシンボリックリンク検査に関するTOCTOU問題をmod_mrubyで解決してみた

共有ホスティングにおいて、各利用ユーザに対してシンボリックリンクの使用を許可していた場合に、他のユーザ領域のコンテンツを閲覧できたりする問題があるため、SymLinksIfOnwerMatchの設定によりリンクのownerとリンク先のファイルのownerが一致していない限りはリンクを辿らないようにする対処がされてきましたが、そもそもリンクの検査とファイル作成のタイミングによっては、検査をすり抜け、閲覧を可能にするTOCTOU問題がありました。

この件に関しては、「Apache HTTPD: `Options -FollowSymLinks` は不完全」に詳しく解説されているのでそちらを見て頂くとして、リンク使用は許可しつつもそのTOCTOU問題を解決するための一つの方法として、open()したコンテンツファイルのfdから情報を検査してownerに不一致があった場合はレスポンスを返さないようにする、というアプローチがあります。

実装として、Apache本体に手を入れたり、Apacheモジュールで対応したり、といくつか方法がありますが、今日はWebホスティングのセキュリテイとmod_mrubyの実装の復習がてら、単純な静的コンテンツへのリクエストにおけるこの問題をmod_mrubyで楽に解決できるようにしてみました。

具体的には、コンテンツがopen()されているアウトプットフィルターフェイズにおいてmod_mrubyで介入し、任意のuidと、open()されているコンテンツのuidが異なっていたらエラーを返す、という処理を書きました。

まずは、Apacheの設定を以下のようにして、

[program lang=”apache” escaped=”true”]

SetOutputFilter mruby
mrubyOutputFilter /path/to/solved_link_toctou.rb

[/program]

/path/to/solved_link_toctou.rbを以下のようにします。

[program lang=”ruby” escaped=”true”]

uid = 1000

f = Apache::Filter.new

if f.uid != uid
  f.error_create Apache::HTTP_SERVICE_UNAVAILABLE
end

[/program]

このように実装することで、f.uidメソッドから既にopen()されたファイルのuid情報を取得できるので、TOCTOU問題を気にすることなくownerのチェックを行えます。上記の例の場合では、openしたコンテンツのuidが1000じゃなければ503エラーを返し、一致すれば何もせずにリクエストのあったコンテンツの内容をそのまま返します。後は幾つか細かい点があるのですが、それを書くと内容が広がりすぎるので一旦はこの程度の検査に留めておきます。

というわけで、mod_mrubyのリハビリがてら、SymLinksIfOnwerMatchTOCTOU問題をmod_mrubyで解決してみました。mod_mrubyだと非常に簡単に書けるので楽でした。