WebDAV時代のセキュリティ対策[後編]


WebDAV時代のセキュリティ対策[後編]
包括的な対策テクニック

前編では各メソッドの動作や制限方法について解説した。後編ではより包括的な手段によるセキュリティ対策を検討する。Apacheの設定が中心だが、mod_dav固有の注意点やSquidを使う方法についても紹介する。

レイヤの概念とセキュリティ

 以前の特集で、「レイヤ4以下を安全にしておけば」(図1)ということを述べました。

図1 下位レイヤをセキュアにすれば上位も安全だが、あくまで一般論

 しかし、レイヤ4以下が「守られた」ネットワークの「中から」のアタックは回避のしようがありません(図2)。

図2 リクエストがレイヤ3や4レベルで通過してしまった(セキュアなネットワークの内部で何らかの不正アクセスが試行される)場合、WebDAVサーバの認証/アクセス制限のみが頼り

 また、セキュリティ製品の中にはIDS(侵入検知システム:)に代表されるような、レイヤ5以上についても通信内容を監査することが可能なものもあります。しかし、そのような製品は概して高価であり、機能的にも必要以上のものが備わっていることが多く、WebDAVサーバに対する攻撃を保護するだけに導入するのでは割高感は否めません。

:IDSについては、不正侵入対策最前線(後編)で詳細に述べられています。

 単にメソッドの種類だけでフィルタをかけるのであれば、リバースプロキシとして利用可能なプロキシサーバ(例えばSquid)を使って、アクセスコントロールリスト(リスト16)を定義したりURLフィルタ(リスト17)を実施する方法も使えます。

acl davaccess method PROPFIND,PROPPATCH,DELETE,MKCOL,PUT,COPY,MOVE ←ACLを定義
http_access allow davaccess 192.168.0.0/24 ←上記ACLに対して192.168.0.0/24なネットワークからは許可
http_access deny all ←それ以外はすべて禁止
リスト16 ACLの定義

acl davaccess url_regex davauth ←davauthという文字列が含まれるURLに対してACLを定義
http_access allow davaccess 192.168.0.0/24 ←上記ACLに対して192.168.0.0/24なネットワークからは許可
http_access deny all ←それ以外はすべて禁止
リスト17 URLフィルタの設定

 オープンソース/商用製品を問わず、いろいろなプロダクトがあるので、手間とコストを考え合わせてベストなものを選んでください。

mod_dav固有のセキュリティ対策

 WebDAV Resourcesには、mod_davの情報が掲載されています。その中にはセキュリティに関する項目も並んでおり、やはり真っ先に認証の必要性が説かれています。それ以外にもmod_dav固有の対策などについて触れられているので、この場を借りて簡単に紹介しておきます。

 基本的に、WebDAVのプロトコル仕様以外でセキュリティホールになり得る部分はXMLパーサ周辺の処理やDepthヘッダの指定の部分です。XMLについては、巨大なXMLボディを送信されることによる資源の占有(DoS)に関して(図3)、Depthヘッダについては、Depth: Infinityを指定してのPROPFINDリクエストでメモリを大量に消費することがある(図4)という点に触れられています。

図3 リクエストが完了するまでメモリは保持されたまま

図4 コレクションの階層が深ければ深いほどダメージ大

 これらの事象は、基本的には避けるような構造(初期設定)になっています。XMLボディのサイズについてはLimitXMLRequestBodyディレクティブの指定(デフォルト値は1000000bytes)で、Depthリクエストの値についてはDAVDepthInfinityディレクティブの指定で変更可能です。ただし、これらのディレクティブを変更した場合、相応のリスクが発生するので気を付けて運用することをお勧めします(もちろん、変更せずに運用できればそれに越したことはありません)。

 もちろん、HTTPリクエストそのもののサイズもApacheの設定で制限することが可能です。この場合、LimitRequestBodyディレクティブを指定しますが、問答無用ですべてのメソッドが制限されます。PUTで書き込むファイルサイズなども自動的に制限されることになるので設定する場合は注意してください。

包括的なメソッド制限の設定

 前編で紹介したメソッド別の対策において、<Limit>ディレクティブを使用した個々のメソッドの制限方法について述べました。

 しかし、今後WebDAVの規格が新たに制定されるにつれてメソッドも増えることは十分考えられます(というか増えます)。その都度新しいメソッドに対する設定を追加してもよいのですが、たいていのメソッドは不特定多数に実行させる性質のものではありません。そこで、<LimitExcept>ディレクティブを利用して特定のメソッド「以外」に制限をかける方法を紹介します。この方法は、mod_dav installation(http://www.webdav.org/mod_dav/install.html)にも紹介されています。そのドキュメントには「指定しないものについては暗に制限をかけたことになる」といった記述があり、メンテナンスの手間を減らす意味でも使用する価値があります。

 例えば、リスト18のように設定するとGET、HEAD、OPTIONS「以外」のメソッドを使う際は認証が必要になります。

<Location /davauth>
       DAV     on
       AuthUserFile     /home/kmiya/htpwd/user.pwd
       AuthGroupFile    /dev/null
       AuthName         DAVhome
       AuthType         Basic
       <LimitExcept GET HEAD OPTIONS> ←GET、HEAD、OPTIONS以外を制限
           Require user wakatono
       </Limit>
</Location>
リスト18 LimitExceptディレクティブによる制限

 この例ではベーシック認証による制限をかけていますが、後述するダイジェスト認証も利用可能です。

 ちなみに、ここまでは各メソッドに制限をかける方向によるブロック方法を説明しましたが、Webサーバのユーザー権限ではリソースを操作できないようにする(例:オーナーを変更する)ことでも防御は可能です。ただし、この場合はWebDAV経由でのリソース操作ができなくなることにもつながるので、絶対に操作しないと確信できるもの以外は権限を変更しないでください。

ベーシック認証の危険性 〜ダイジェスト認証の勧め〜

 ここまでは危険性をはらんだ個々のメソッドの性質について説明してきましたが、メソッド利用を許可/禁止するのは認証ベースで実施するのが通常です。認証については、大きく分けてベーシック認証とダイジェスト認証の2つあり、よく利用されるのはベーシック認証ですが、それぞれ以下のような特徴があります。

盗聴に弱いベーシック認証

 ベーシック認証について、認証部分のキャプチャを行った結果をリスト19に示します。

PROPFIND /davauth HTTP/1.1
Accept-Language: ja, en-us;q=0.2
Content-Type: text/xml
Translate: f
Content-Length: 380
Depth: 1
User-Agent: Microsoft Data Access Internet Publishing Provider DAV 1.1
Host: tripmachine
Connection: Keep-Alive
Authorization: Basic d2FrYXRvbm86d2FrYXBhc3M=

<?xml version="1.0" ?>..<propfind xmlns="DAV:">
<prop>
(中略)
</prop>
</propfind>
リスト19 ベーシック認証の様子。AuthorizationヘッダにIDとパスワードが含まれている

 HTTPヘッダにAuthorizationヘッダがあるのが分かります。ベーシック認証では、IDとパスワードはMIMEでエンコーディングされているだけです。これを解読してみましょう。結果をリスト20に示します。

$ mimencode -u
d2FrYXRvbm86d2FrYXBhc3M=
wakatono:wakapass ←IDがwakatono、パスワードがwakapass
リスト20 mimencodeコマンドによる解読

 このように、IDとパスワードは簡単に解読されてしまいます()。

:SSLなどによる暗号化が行われている場合は、ペイロード全体が暗号化されているため、これほど簡単には分かりません。

Apache 1.3.22のダイジェスト認証対応化

 ダイジェスト認証はRFC 2069で規定され、Apache 2.0の新機能としても挙げられるもの(Apache 2.0の新機能とその実力にて、ダイジェスト認証については説明されています)ですが、Apache 1.3.8からexperimentalということでmod_auth_digest.cという名前でダイジェスト認証モジュールが同梱されています。

 Linuxでの簡単なインストール手順を以下に示します。

1. Apache 1.3.22を展開し、

$ cd apache_1.3.22/src/modules/experimental

を実行する

2. apxsを用いて、mod_auth_digest.cをコンパイルする

 乱数生成に/dev/randomを用いるため、コンパイル時に以下のように-D DEV_RANDOMというパラメータを付加する

$ /usr/local/apache/bin/apxs -c -D DEV_RANDOM mod_auth_digest.c
gcc -DLINUX=22 -DUSE_HSREGEX -DUSE_EXPAT -I../lib/expat-lite -fpic -DSHARED_MODULE -I/usr/local/apache/include -D DEV_RANDOM -c mod_auth_digest.c
gcc -shared -o mod_auth_digest.so mod_auth_digest.o

3. インストール

$ /usr/local/apache/bin/apxs -i mod_auth_digest.so

4. httpd.confに以下の内容を付加する

LoadModule digest_auth_module libexec/mod_auth_digest.so
AddModule mod_auth_digest.c

5. Apache再起動

$ /usr/local/apache/bin/apachectl restart

 インストール後、リスト21のような内容をhttpd.confなどに設定することで(httpd.confを変更した場合は再起動が必要)、ダイジェスト認証が有効になります。

<Location /davauth>
        DAV     on
AuthDigestFile     /home/kmiya/htpwd/user.dig
AuthName        editor
AuthType         Digest
<Limit PUT POST DELETE PROPFIND PROPPATCH MKCOL COPY MOVE LOCK UNLOCK>
           Require user wakatono
</Limit>
リスト21 /davauthに対するダイジェスト認証の設定例

wakatono:editor:6a61041820aa2912c622b655a2e28725
/home/kmiya/htpwd/user.digの内容

 ダイジェスト認証に必要な要素は、ユーザーIDとrealmという概念です。同じID、realm、パスワードで複数回の認証を行った際に取得したパケットキャプチャ中の認証関係ヘッダをリスト22に示します。ダイジェストはリスト中のような形で表記され、IDやrealmは平文で流れるものの、パスワードに関連したダイジェストは常に一定というわけではないため、ベーシック認証のように即破られるということはありません。

Webフォルダから送信(1)
Authorization: Digest username="wakatono", realm="editor",
 qop="auth", algorithm="MD5", uri="/davauth",  
nonce="7RAFPA==89f8284a75d5ff7c52b7fae802670b1ffb23d5f2",
 nc=00000004,   cnonce="c5ab0def2461729996ce86378568abfe",
 response="6d7a5ff5269bddc43e8ec5b11fa1bfb9"

Webサーバから送信(1)
Authentication-Info: rspauth="f9779fd5ca1bd267fbd46c0b755f5735", 
cnonce="c5ab0def2461729996ce86378568abfe" , nc=00000004, qop=auth

Webフォルダから送信(2)
Authorization: Digest username="wakatono", realm="editor",
 qop="auth", algorithm="MD5",  uri="/davauth", 
nonce="7RAFPA==89f8284a75d5ff7c52b7fae802670b1ffb23d5f2", 
nc=00000005,  cnonce="69aec56aebe4ffa2e7d5e19411e49359", 
response="7bbe5be65a70501a04b260349a9bf215"

Webサーバから送信(2)
Authentication-Info: rspauth="da8361aff94fd407447234bfa91197cc", 
cnonce="69aec56aebe4ffa2e7d5e19411e49359" , nc=00000005, qop=auth

DAV Explorerから送信
Authorization: Digest realm="editor",username="wakatono",
uri="/davauth/",
nonce="xRIFPA==777d0f40ee9b607c61b4db7058c2c29562689ee4",
response="08d8cf253c59b255ab6a19c2e1fe37f5",algorithm="MD5",
cnonce="40b011027aba8e2a3704d9090859dee5",qop="auth",nc="00000001"

Webサーバから送信
Authentication-Info: rspauth="7da56fc4c30aae234f3f98d3033ddb2e", 
cnonce="40b011027aba8e2a3704d9090859dee5" , nc=00000001, qop=auth
リスト22 ダイジェスト認証を使った場合。認証情報を含んだ文字列が不定になっているのが分かる

 試した範囲では、IE5、cadaver、DAV Explorerの最新版ではダイジェスト認証が利用可能なようです(SkunkDAVでは不可)。

まとめ

 これまで述べてきた内容からお分かりいただけるかと思いますが、WebDAVの場合はCGIなどと違ってメソッドそのものを通すことがセキュリティホールになり得ます。また、サーバ自体にWebDAV機能があるような場合、通常のWebサーバに対する以上にコンテンツの改ざんや破壊が容易であり、WebDAV機能そのものに対する取り扱いも慎重に行うべきであるといえるでしょう。

 例えばFTPプロトコルなどでもこの部分は同様ですが、FTPの場合はこれまで長い期間使われる中で、どのような設定を行うべきか、どのようなセキュリティホールがあるか(もしくは新規に出現したか)という部分についてWebDAVよりまとまっているといえます。WebDAVについても、使われていくうちにこのような情報はまとまってくると思われますが、いま現在利用されている部分の仕様を把握し、適切な制限をかけることで必要十分なセキュリティを確保することは可能です。

 WebDAVは「書ける」プロトコルです。だれでも「書ける」ことによるデメリットを把握し、適切な人だけ「書ける」ようにすることで、より便利に安全に利用しましょう。

WebDAV日本語情報について

 ちょっと今回のテーマからは外れますが、最近日本語リソースを整備しようという動きが見られます。以下に2つ紹介しますので、興味のある方はのぞいてみてはいかがでしょうか。後者には、以前紹介したmod_encodingのその後や本家WebDAV Resourcesに掲載されているFAQの翻訳などもあります。

Appendix

今回利用したツール 〜Network Grep〜

 筆者は普段linux_snifferやtcpdumpなどを使うのですが、認証時のパケットキャプチャを効率よく行うため、今回はNetwork Grep(以下ngrep)というツールを利用しました。イメージとしてはtcpdump+grepという感じのツールで、特定の文字列パターン(正規表現による記述が可能)を含むパケットのみを表示してくれるというものです()。

:tcpdumpがTCP/IP以外のプロトコルにも対応しているのに対し、ngrepはTCP/IPにしか対応していない点に注意。

ngrepのインストール

 ngrepはlibpcapに依存するので、ngrepを利用するにはlibpcapも必要です。それぞれ以下の場所で入手できます。

 必要なファイルがそろったら、libpcap→ngrepの順序でコンパイル&インストールを行います。

libpcapのコンパイル&インストール

1.libpcapのアーカイブを展開し、作成されたディレクトリ下に移動

$ tar xvzf libpcap-0.6.2.tar.gz
$ cd libpcap-0.6.2

2../configureの実行

$ ./configure

3.コンパイル&インストール

$ make
$ su root
Password:
# make install

ngrepのコンパイル&インストール

1.ngrepのアーカイブを展開し、作成されたディレクトリ下に移動

$ tar xvzf ngrep-1.40.tar.gz
$ cd ngrep

2. ./configureの実行

$ ./configure

3. コンパイル&インストール

$ make
$ su root
Password:
# make install

まずは使ってみよう

 /usr/bin/ngrepを以下のオプション付きで実行してみましょう。なお、実行にはroot権限が必要です。eth0は、それぞれのネットワークインターフェイス名に応じて変更してください()。

:Linuxの場合、たいていはeth0です。

# /usr/bin/ngrep -d eth0

 すると、以下のような出力が得られることと思います。

# ngrep -d eth0
interface: eth0 (10.1.87.0/255.255.255.0)
##
T 10.1.87.157:23 -> 10.1.87.156:4223 [AP]
  interface: eth0 (10.1.87.0/255.255.255.0)..
##
T 10.1.87.157:23 -> 10.1.87.156:4223 [AP]
  ##..T 10.1.87.157:23 -> 10.1.87.156:4223 [AP]..  interface: 
eth0 (10.1.87.0/255.255.255.0)..
                                                   ..
##
T 10.1.87.157:23 -> 10.1.87.156:4223 [AP]
  ##..T 10.1.87.157:23 -> 10.1.87.156:4223 [AP]..  ##..T 
10.1.87.157:23 -> 10.1.87.156:4223 [AP]..  interface: 
eth0 (10.1.87.0/
  255.255.255.0)..                                 ..
                                                   ..
                                                       ..
exit
6 received, 0 dropped

実際に文字列パターンなどを指定したキャプチャの仕方

 例えば、FTPなどでログイン時のシーケンスを取得するオプションは以下のようになります。

# /usr/bin/ngrep -d eth0 -iA 2 'user|pass' tcp port 21

 これにより、ユーザーID入力時やパスワード入力時から2パケットを表示するようになります。

 では、上記のコマンドでどのようにキャプチャされるかを見てみましょう。まず、FTPクライアントからの操作経過を以下に示します。

C:\>ftp tripmachine
Connected to tripmachine.
220 tripmachine.ts.u.coe.nttdata.co.jp FTP server ready.
User (tripmachine:(none)): samplea
331 Password required for samplea.
Password:
230 User samplea logged in.

 これをパケットキャプチャすると、以下のようになります。

# ngrep -d eth0 -iwA 2   'user|pass'  tcp port 21
interface: eth0 (10.1.87.0/255.255.255.0)
filter: ip and ( tcp port 21 )
match: ((^user|pass\W)|(\Wuser|pass$)|(\Wuser|pass\W))
######
T 10.1.87.156:2769 -> 10.1.87.157:21 [AP]
  USER samplea..                              ←ユーザーID
#
T 10.1.87.157:21 -> 10.1.87.156:2769 [A]
#
T 10.1.87.157:21 -> 10.1.87.156:2769 [AP]
  331 Password required for samplea...
##
T 10.1.87.156:2769 -> 10.1.87.157:21 [AP]
  PASS passsamp..                             ←パスワード
#
T 10.1.87.157:21 -> 10.1.87.156:2769 [AP]
  230 User samplea logged in...

 パケットキャプチャの結果を見ると、ユーザーIDとパスワードが取得されているのが分かります。

 ngrepにはさまざまなコマンドラインオプションがあります。ただ、基本的にはgrepのオプションの一部(-Aや-w)、正規表現による文字列指定、tcpdumpによるプロトコルやポート、アドレス指定の方法などが使えるため、ここでは特に触れません。