Apache HTTPD: `Options -FollowSymLinks` は不完全 - ダメ出し Blog

2013-09-03(Tue) [apache][security] [更新履歴]

シンボリックリンク攻撃を防ぐための Apache HTTPD モジュールの解説はこちら:

背景

ロリポップの共有 Web サービス下のサイト改ざん事件で、 攻撃手法の一つとして 「他ユーザー所有のファイルへのシンボリックリンクを自分のコンテンツディレクトリ下に作り、Apache HTTPD 経由でアクセスする」手順が利用されたらしい。

参考:

徳丸さんのページで解説されているように、Web サーバーが Apache HTTPD であれば、 防ぐ方法として次の対策が挙げられる。

問題点

しかし、シンボリックリンク機能を持つ UNIX には 「ファイルがシンボリックリンクかどうかの判定」、 「ファイルまでのパスがシンボリックリンクかどうかの判定」、 「ファイルのオープン」を一度に処理する方法がないため、 この攻撃を完全には防くことはできない。

実際 Apache HTTPD は、シンボリックリンクを拒否するために次のように動作する。

  1. ファイルがシンボリックリンクかどうか検査する。
  2. ファイルの親ディレクトリがシンボリックリンクかどうか検査する。
  3. ルートディレクトリまで 2 を繰り返し。
  4. ファイルをオープンしてクライアントに返す。

各処理の間にはわずかながら別プロセスが動作する猶予があるため、 このタイミングでファイルまでのパスをシンボリックリンクに差し替えることで、 Apache HTTPD にシンボリックリンクを辿らせることができてしまう。

この問題は、Apache HTTPD の OptionsFollowSymLinksSymLinksIfOwnerMatch の解説に簡単ながら触れられている。 http://httpd.apache.org/docs/2.4/ja/mod/core.html#options :

このオプションを省略したからといってセキュリティの強化にはなりません。 なぜなら symlink の検査はレースコンディションを引き起こす可能性があり、 そのため回避可能になるからです。

Twitter で @a4lg さんに教えてもらったのだが、 この問題は「TOCTOU」(もしくは「TOCTTOU」、「Time Of Check to Time Of Use」) と呼ばれる問題の一種とのこと。原理は知っていたが、名前が付いているのは知らなかった…。

同じく Twitter で @tnozaki さんに TOCTTOU について具体的に解説していて参考になるページを紹介してもらった。

対策

というわけで、この攻撃の根本的な対策方法は「シンボリックリンクを作らせない」 もしくは「シンボリックリンクを無効もしくは使用不可にする」しかない。

具体的には次のような対策が挙げられる。

もちろん、「ユーザーごとに別権限の Web サーバーを立ち上げる」や 「ユーザーごとに別権限でコンテンツをアクセスする Web サーバーにする」 といった方法でも回避できると思う。 後者の実装としては mod_process_security がよさげな印象。

おまけ

さくらインターネット社長の田中さんが「うちは対策済み」とツイートしているが、 上記の解説の通り、わずかながら穴があるはず。よって対策は不完全だと思う。 誰か検証してくれないかな。 さくらインターネットは Apache HTTPD を改造し、 静的コンテンツファイルをオープンしたあとにファイル所有者を検査するようにして対処したとのこと。 open(2) 等で得たファイル記述子に対し fstat(2) してファイル所有者を得てるものと思われる。

さくらインターネットのは default-handler を置き換えるモジュールらしいので、 対抗して出力フィルター版のモジュールを作ってみた。 default-handler は Apache HTTPD のバージョンによって処理内容が微妙に 異なるので、フィルター版のほうが互換性と汎用性が高いと思う。

Linux 2.6.39 以降で実装されているという open(2) の O_PATH オプションを利用すればシンボリックリンクを直接開くことが可能で、 これと fstatat(2) と AT_SYMLINK_NOFOLLOW フラグ、 openat(2) と O_NOFOLLOW フラグを組み合わせれば、 シンボリックリンクを避けたファイルのオープンが実装可能な様相。 Linux の特定バージョン依存とはいえ、有効な対策となりそう。

TODO: Symlink Busters プロジェクト のネタとして実装する予定。

適当に書いた攻撃用のコードを晒しておく。あまり効率がよくなく、 CPU 負荷をかけるので注意。Linux であれば inotify(7) で監視するなどして、 Apache HTTPD が stat(2), lstat(2) しに来たタイミングでダミーを消して シンボリックリンクを貼るのがいいと思う。