WAF Tech Blog

クラウド型WAF「Scutum(スキュータム)」の開発者/エンジニアによるブログです。
金床“Kanatoko”をはじめとする株式会社ビットフォレストの技術チームが、“WAFを支える技術”をテーマに幅広く、不定期に更新中!

2016年2月

  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29          
Scutum開発者/エンジニアによる技術ブログ WAF Tech Blog

ScutumのゼロデイHashDoS対策と、JavaのXMLパーサ実装

はじめに

数年前に発見されたHashDoSですが、実際に攻撃が行われているケースは殆ど目にしません。ソフトウェアのロジックを突くDoSとしては、他にApacheに対してRangeヘッダを使う攻撃手法(Apache Killer)なども発見されましたが、こちらも同様に、殆ど実際の攻撃としては検知していません。DoS攻撃にはもっと単純で、攻撃対象のアーキテクチャを調べる必要のない、シンプルな物量攻撃が好まれているような印象です。そのため筆者としてはHashDoSのセキュリティリスクは非常に低いものという認識です。

さて現在、国内ではあまりニュースになっていませんが、いくつかのミドルウェアにおいてHashDoS対策の漏れが発見されており、対策の実装が進められているフェーズのようです。これらのミドルウェアにおけるHashDoS攻撃の対策をScutumでは既に実施済みで、「ゼロデイ攻撃への対策」ができている状態です。情報がパブリックになった時点で、また続報をお届けします。

Javaでの対策

今回、あらためていくつかの場面でのHashDoS対策について調べているうちに、いくつか面白い実装を見かけたので、このエントリで簡単に紹介したいと思います。まずJavaですが、普通にJVMを起動すると、HashMapやHashTableがHashDoSに脆弱であることがわかっています(これは仕様です)。しかしjdk.map.althashing.thresholdというシステムプロパティを使うことで対策ができる、とOracleのサイトで説明されています。HashMapクラスの実装において、特に、実際の攻撃に使われそうであるStringクラスについてのみハッシュ値計算を特別に行っていたりするのが興味深いです(JDK1.7で確認)。

Java XMLパーサ(Xerces)での対策

Javaに含まれているXMLパーサの実体は、ApacheのソフトウェアであるXercesです。内部の実装において、JavaのHashMap等を使ってもよさそうな場面において、独自実装のクラスであるSymbolTable等が使われています。SymbolTableではHashDoSへの対策として、こちらの実装がなされています。このSymbolTableクラスの実装は面白いもので、ハッシュ値の衝突を監視し、ある閾値を超えたらハッシュ値の計算ロジックを変更する形になっています。それぞれのインスタンス内部でランタイムにおいてこの処理が行われるので、攻撃者がパフォーマンス劣化をモニタリングしながら衝突を起こそうとブルートフォース的に攻撃しても、それをある時点で無効にすることができる、優れた実装になっています。

テストコード

上記の2点について、テストするためのコードを書いてみました。実行するためにはXercesのライブラリ(jarファイル群)が別途必要になります。

ハッシュ値が衝突する文字列を大量に用意し、それをSymbolTableやHashSetに追加していき、どの程度の時間がかかるかを出力します。上記の対策が施されていないバージョンのXercesでテストすると、出力は以下のようになります。

Testing SymbolTable...
0 ms
29 ms
69 ms
90 ms
127 ms
146 ms
181 ms
212 ms
248 ms
251 ms
281 ms
310 ms
345 ms
374 ms
402 ms
435 ms
449 ms
160 ms
162 ms
202 ms
200 ms
240 ms
271 ms
312 ms
311 ms
361 ms
409 ms
452 ms
529 ms
598 ms
689 ms
678 ms
771 ms
TOTAL: 10903 ms

対策済みのバージョンでは、以下のようになります。

Testing SymbolTable...
0 ms
22 ms
49 ms
76 ms
104 ms
121 ms
148 ms
174 ms
198 ms
195 ms
219 ms
237 ms
272 ms
286 ms
315 ms
340 ms
385 ms
356 ms
344 ms
98 ms
2 ms
1 ms
0 ms
1 ms
1 ms
0 ms
1 ms
1 ms
0 ms
1 ms
1 ms
0 ms
1 ms
TOTAL: 3951 ms

途中でハッシュ値の計算ロジックが変更され、いきなり速くなっていることがわかります。

次にHashSetについてです。jdk.map.althashing.thresholdの指定なしだと、下記のように時間がかかります。

Testing HashSet...
1 ms
6 ms
13 ms
20 ms
29 ms
35 ms
43 ms
51 ms
59 ms
66 ms
74 ms
81 ms
89 ms
98 ms
106 ms
113 ms
121 ms
131 ms
138 ms
156 ms
158 ms
182 ms
188 ms
218 ms
233 ms
273 ms
405 ms
402 ms
316 ms
351 ms
376 ms
410 ms
509 ms
TOTAL: 5851 ms

しかしjdk.map.althashing.thresholdの指定を行うと、下記のように速くなります。

Testing HashSet...
0 ms
4 ms
2 ms
2 ms
1 ms
2 ms
1 ms
3 ms
1 ms
2 ms
2 ms
1 ms
1 ms
1 ms
1 ms
0 ms
1 ms
1 ms
0 ms
0 ms
1 ms
1 ms
0 ms
1 ms
0 ms
2 ms
0 ms
0 ms
0 ms
1 ms
0 ms
0 ms
0 ms
TOTAL: 35 ms

まとめ

今回はJavaとXercesにおける、それぞれ異なったHashDoS対策について紹介しました。ご感想などあればお気軽に@kinyukaまでお寄せください。