共有WebホスティングでVitrualHost設定に手を入れずにシンボリックリンクのTOCTOU問題を解決するApacheモジュールを作った

共有Webホスティングを提供している業者の皆様は、シンボリックリンクのTOCTOU問題というかなりクリティカルで必ず対応すべき問題をおそらく様々な方法によって解決されていると思います。

しかし一方で、TOCTOU問題をなんとなく放置されている、あるいは、誤解されている場合もあるかもしれません。この問題は、Apacheでシンボリックリンクを使えない(シンボリックリンクにアクセスがあったら4系エラーを返す)設定にしていたとしても、シンボリックリンクファイルを作る事さえできれば、攻撃プログラムを使う事により、apache権限でアクセス可能なファイルにシンボリックリンク経由でアクセスすることが可能となります。また、Apache本体のコアに直接パッチを当てて対応されている方も多いでしょう。

その場合、

  • コアのパッチは今後Apacheのバージョンアップに伴いメンテが面倒になる
  • 幾つか公開されているモジュールは設定の変更箇所が多いので困っている
  • TOCTOU問題をあまりよくわかってない

という状況があると思います。

そこで、あまり既存の共有ホスト、例えばVirtualHostの設定等を変える事なく簡単な設定を追記するのみで共有WebホスティングのTOCTOU問題を解決するApacheモジュールmod_fileownercheckを作りました。

追記1:SetOutputFilterの設定をしなくて良いようにした

インストールは簡単で、apxsでビルドした後いつも通りモジュールをLoadModuleするだけで良いです。

モジュールをロードするだけで、シンボリックリンクのTOCTOU問題を解決できます。ホスティング業者によっては、共有ホスティングの構成が様々でこのモジュールだけでは対応出来ない場合もあると思います。ですので、一度攻撃プログラムを用いて試した上で利用するのが良いと思います。VirtualHost + suEXEC + Directoryのパーミッションのような定番の共有ホスティングの場合は、大抵このモジュールだけで解決できると思います。

仕組み的には単純で、リクエストのあったファイルがApacheによってopenされた時点で、そのopenされたファイルのownerとリクエストのあったファイルパスのownerが一致するかをチェックするものです。

追記2:.htaccessでの上書き防止処理やリクエストパスの途中にシンボリックリンクを含んでいる場合の対処を追加

さらに、.htaccessで上書きできないようモジュールで強制的にoutput filterに追加するようにしました。また、リクエストパスの途中に他のユーザのディレクトリを指すシンボリックリンクが含まれている場合も対応するために、suEXECで設定しているownerとopenしているファイルのownerチェックも行うようにしました。

もしまだApacheのバーチャルホスト等で構築した共有Webホスティングを提供しながら、このTOCTOU問題の対応をされていなかったり、コアに手を入れていたりする場合は是非このmod_fileownercheckをご活用下さい。また、問題がありそうでしたらレポジトリにissueやpull-requestをして頂けると幸いです。

apache bench(ab)コマンドをRubyで拡張できるab-mrubyのDockerイメージを公開

ab-mrubyとはapache bench(abコマンド)のパラメータ設定や、ベンチマーク結果のテストをRubyで書けるベンチマークツールです。今回はそのab-mrubyコマンドのDockerイメージを作りました。

僕みたいにabコマンドを毎日のように使うエンジニアにとっては、ab-mrubyはそれなりに有用だと思っていたのですが、mrubyをビルドしたりaprを使ってabをビルドしたりいまいち使いにくいなぁと思っていました。しかしそれがDockerの登場によって面倒な所はほとんどラッパーできるようになり、ab-mrubyのDockerイメージを作る事によって必要な部分だけを弄ってすぐにab-mrubyを使えるようにしました。

自身のクライアントPCにDockerを入れるのは気軽にできると思うので是非一度遊んでみてください。

ab-mrubyについてご存じない方は以下のエントリを御覧ください。

最新のab-mrubyはGitHubにて公開しています。

簡単な使い方

ab-mrubyに関しては、上記のエントリに書いているので、今回は簡単な使い方の説明のみにします。

まずはab-mrubyのDockerイメージをDocker Hubからpullして下さい。

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

sudo docker pull matsumotory/ab-mruby

[/program]

そして、実際に実行してみましょう。docker runコマンドに加えて最後の引数にURLを指定してやるだけでab-mrubyを試すことができます。

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

$ sudo docker run matsumotory/ab-mruby https://blog.matsumoto-r.jp/
======================================================================
This is ab-mruby using ApacheBench Version 2.3 <$Revision: 1430300 $>
Licensed to MATSUMOTO Ryosuke, https://github.com/matsumoto-r/ab-mruby

                          CONFIG PHASE

======================================================================
  Target Information  URL: https://blog.matsumoto-r.jp/
  Target Information HOST: blog.matsumoto-r.jp
  Target Information PORT: 80
  Target Information PATH: /
  Target Information  SSL: false
======================================================================
This is ab-mruby using ApacheBench Version 2.3 <$Revision: 1430300 $>
Licensed to MATSUMOTO Ryosuke, https://github.com/matsumoto-r/ab-mruby

                            TEST PHASE

======================================================================
[TEST CASE] [true] FailedRequests (0) should be 0
[TEST CASE] [true] WriteErrors (0) should be 0
[TEST CASE] [true] CompleteRequests (100) should be 100
[TEST CASE] [true] TransferRate (1179.60901260841) should be over 500
[TEST CASE] [false] RequestPerSecond (18.4716961893629) should be over 1000
[TEST CASE] [true] TimePerRequest (54.1368799999999) should be under 100
[TEST CASE] [true] TimePerConcurrentRequest (541.368799999999) should be under 3000
[TEST CASE] [true] ConnetcErrors (0) should be 0
[TEST CASE] [true] ReceiveErrors (0) should be 0
[TEST CASE] [true] LengthErrors (0) should be 0
[TEST CASE] [true] ExceptionsErrors (0) should be 0
[TEST CASE] [true] Non2xxResponses (0) should be 0

test suites: [false]

[/program]

すると、予めRubyで記述したベンチマークパラメータとテストケースによって実行されたベンチマークとテストの結果が得られます。おおおお、RequestPerSecondがテストケースの値より低く出ているのでテストが失敗していますねー、とかがわかります。

Rubyでベンチマークパラメータやテストを書いて自分専用のDockerイメージを作る

ab-mrubyのDockerイメージはab-mruby/docker/ディレクトリ以下のRubyで書かれたベンチマークパラメータとテストケースのサンプルを元に実行されています。それらを自分なりに変更したい場合は、

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

git clone https://github.com/matsumoto-r/ab-mruby.git
cd ab-mruby
vi docker/ab-mruby.conf.rb
vi docker/ab-mruby.test.rb
sudo docker build -t local/ab-mruby .
sudo docker run local/ab-mruby https://blog.matsumoto-r.jp/

[/program]

ab-mruby.conf.rbにベンチマークパラメータを、ab-mruby.test.rbにテストケースを書くと、自分なりのベンチマークパラメータやテストケースを反映した自分専用のDockerイメージを作成する事ができます。自由にGitHubからforkして頂き、そこからDocker Hubと連携してベンチマークイメージを作るのも良いと思います。

書き方は、

上記に上げたab-mrubyに関するエントリやab-mrubyのGitHubレポジトリを参考にして下さい。

ab-mrubyのDockerイメージを使って有意義なベンチマークライフを過ごしてみてはいかがでしょうか!

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だと非常に簡単に書けるので楽でした。

apxsを使ってApacheのディレクトリ構成を動的に取得

もはやApache httpdのディレクトリ構成やファイル配置はディストリビューションによって全く異なってきているのが現状です。

そういう状況で、システムにインストールされているApacheの設定に対して、なにかアクションを起こしたい場合は、システム依存のパスをスクリプトに直接かいている人もいるかもしれません。

そこで今回は、apxsコマンドさえ特定できれば、Apacheのディレクトリ構成やファイルの配置を動的に取得できるので、それを試してみましょう。

続きを読む