日本のStruts1の脆弱性(CVE-2014-0114)対応は素早く粛々と行われているようで、なによりです。
こちらの記事で
- BeanUtilsのResolverを差し替える方法
- RequestProcessorを差し替える方法
について書きました。
どちらもJavaアプリケーションレイヤでの対策ですが、ModSecurityを使ってもキチンと穴塞げるようなのでやってみました。これはAPサーバの前にApacheがいることが前提になりますが、対策は非常に簡単です。
ModSecurityのセットアップ
Ubuntu系は、aptでインストールできます。
% sudo apt-get install libapache2-modsecurity
そうすると、/etc/modsecurity/modsecurity.conf-recommended
にコンフィグのexampleが置かれているはずなので、これをコピーして編集します。
% sudo cp /etc/modsecurity/modsecurity.conf-recommended /etc/modsecurity/modsecurity.conf
ファイル最後尾にでも、
SecRule ARGS_NAMES "(^|\.)[cC]lass\." "deny,log,status:403"
を追加します。これはclass,Classを含むネストしたパラメータの場合、そのリクエストを拒否し、ログを書き、403でレスポンスを返すというルールです。詳しくはリファレンスマニュアルをみてください。
https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual
/etc/apache2/mods-available\mod-security.conf
中に、Include "/etc/modsecurity/*.conf"
の記述があるので、後はモジュールを有効化して、再起動するだけです。
% sudo a2enmod mod-security
% sudo apache2ctl restart
テスト
それではこのルールが有効かどうか試してみます。あとでマルチパートのリクエストもテストするので、そういうときはJMeterを使うと便利です。
スレッドグループにHTTPリクエストのサンプラーを追加し、パスやパラメータを設定します。
これを"結果をツリーで表示"のリスナーを追加して実行すると、実行結果が見れます。
ちゃんと、403で弾かれてますね!
さて、問題はServletFilterでは難しかったMultipartのパラメータもチェックできるか、です。mod_securityはMultipartも一度パースして、再構成して後ろのフックに回す機能があります。JMeterでマルチパートのリクエストを作るのはチョー簡単です。以下のようにメソッドをPOSTにしてMultipartのチェックボックスにチェックつけるだけです。
さて、こちらも実行してみます。
ヤリました!マルチパートでもちゃんと403が返りルールにしたがってチェックかかっていることが分かります。
正しくModSecurityが動いた結果かどうかは、auditログが出力されているので、そちらで確認できます。
[03/May/2014:00:25:06 +0900] U2O40n8AAQEAADImF4IAAAAB 127.0.0.1 32834 127.0.0.1 80
--de079d50-B--
POST / HTTP/1.1
Connection: keep-alive
Content-Length: 238
Content-Type: multipart/form-data; boundary=ioU4y3E5yzasxHc7HgXGrFwUCF9AKmWVKAHv5Zh
Host: localhost
User-Agent: Apache-HttpClient/4.2.6 (java 1.5)
--de079d50-I--
class%2eclassLoader%2ehoge=hehehe
--de079d50-F--
HTTP/1.1 403 Forbidden
Vary: Accept-Encoding
Content-Length: 277
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html; charset=iso-8859-1
--de079d50-E--
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>403 Forbidden</title>
</head><body>
<h1>Forbidden</h1>
<p>You don't have permission to access /
on this server.</p>
<hr>
<address>Apache/2.2.22 (Ubuntu) Server at localhost Port 80</address>
</body></html>
--de079d50-H--
Message: Access denied with code 403 (phase 2). Pattern match "(^|\\.)[cC]lass\\." at ARGS_NAMES:class.classLoader.hoge. [file "/etc/modsecurity/modsecurity.conf"] [line "207"]
Action: Intercepted (phase 2)
Stopwatch: 1399044306059830 1099 (- - -)
Stopwatch2: 1399044306059830 1099; combined=91, p1=34, p2=44, p3=0, p4=0, p5=13, sr=0, sw=0, l=0, gc=0
Response-Body-Transformed: Dechunked
Producer: ModSecurity for Apache/2.6.3 (http://www.modsecurity.org/).
Server: Apache/2.2.22 (Ubuntu)
--de079d50-J--
Total,0
--de079d50-Z--
こんな感じでログが出力されています。大丈夫そうですね。
ModSecurityによるオーバーヘッド
さて、既存の環境にModSecurityを適用する場合、気になるのはその性能オーバーヘッドです。
せっかくのJMeterなのでスループットを測ってみます。サーバサイドの処理はなく普通のApacheのエラーを返すだけの処理です。
添付ファイル(500kb)あり (100スレッド/無限ループ)
ModSecurity | スループット(/sec) | 90%ライン (msec) |
---|---|---|
有(正常リクエスト) | 527 | 297 |
有(正常リクエスト) | 504 | 335 |
無 | 1147 | 6 |
添付ファイルなし (100スレッド/無限ループ)
ModSecurity | スループット(/sec) | 90%ライン (msec) |
---|---|---|
有(正常リクエスト) | 6415 | 8 |
有(攻撃リクエスト) | 4444 | 37 |
無 | 7199 | 1 |
マルチパートをパースして再構成するので、やはりマルチパートでのリクエストが多い場合は、結構なオーバーヘッドになるようです。が、Apacheに余裕があってファイルアップロードのリクエストがそんなに殺到するものでない場合においては、90%ラインは実用的な数値なので、ModSecurityの導入検討の価値はあるかと思います。