Node.js で発生した Hash flooding DoS とその内容について
Node.js のセキュリティアップデート
7/11 に Node.js のセキュリティアップデートがリリースされました。
Security updates for all active release lines, July 2017 | Node.js
これには複数の脆弱性が報告されており、今回はそのうちの1つの Hash flooding DoS という脆弱性が何なのか、それに対して採用された対策が何なのかについてお話します。
Hash flooding DoS (hashdos)
Denial Of Service 、つまりサービス拒否攻撃の一種です。 JavaScript のオブジェクトは内部的にハッシュテーブルとして表現されています。
ハッシュ関数は同じkeyなら同じ値を返しますが、別なkeyなら通常は別な値になります。
ハッシュテーブルのinsert, get, update, removeそれぞれ、通常時は O(1) でアクセスできることが期待されていますが、例外があります。ハッシュ値がぶつかり、 別なkeyにも関わらず、同じ値になった際(コリジョンした際) に、内部的にリストとして扱い、リストに追加させて持つ、という動きをします。ちなみにこれをハッシュの連鎖法と言います。
ハッシュ関数の偏りを狙って意図的に同じハッシュ値を持つ key を発生させる事で、O(1) で取得できていたはずの key の取得が O(n) となり、 複数(n個)のkeyを一度に取得しようとした場合は O(n2) になります。こうなるとアクセスする度に CPU使用率が向上し、結果として DoS になるという攻撃です。
V8 の Hash flooding DoS 対策 について
V8 の hash はこの Hash flooding DoS に関しては既に対策されており、ハッシュ関数の seed 値を乱数化することで、ハッシュ関数の偏りを推測することは困難になるように設計されています。
その対策は Node.js の過去の対応でも対応されていました。
しかしながら、今回はこの Hash flooding DoS が 「条件付きで発生してしまう」 という事になりました。
v8-snapshot という機能
Node.js のJavaScriptエンジンである v8 には heap のスナップショットを取るという機能が実装されています。 JavaScript には builtin object として色々なオブジェクトを作成するため、そもそもそのsetupに時間がかかります。このsetup時間を短縮させるために、 heap のスナップショットを build 時に作って serialized した状態で管理し、起動時にはこれを deserialized する改善が行われています。
これにより、起動時間を短縮する効果を産んでいますが、今回はこの機能が結果として hash の乱数化したはずの seed もserializedしてしまい、対策した結果が無効になる、という問題が起きてしまいました。
ソースからビルドすれば、ビルドのタイミングでは乱数化されるので、hashのseed値を推測することは困難になりますが、 Node.js はビルド済みのバイナリを公式で配布しており、この配布されたバイナリの中にはseed値を含んだsnapshotが入っているため、 Hash Flooding DoS が起きうるということで、 high severity vulnerabirity
として対策されることになりました。
セキュリティアップデートでは既にbuild時に v8-snapshot を取らないように対策がされています。
どうしたらいいのか
既にパッチが公開されているのでアップデートして下さい。
Node v8.1.4 (Current) | Node.js
Node v4.8.4 (Maintenance) | Node.js
v4 以前の方は早めにv4以上にアップデートをおすすめします。
まとめ
- Hash Flooding DoS (通称hashdos) の紹介
- V8 の対策とv8-snapshotの話
- セキュリティ対策
special thanks
今回はConstさんからv8 snapshotの話の解説をいただき記事にしました。