xpressengine / xe-core

XpressEngine 1.x
https://xe1.xpressengine.com
Other
89 stars 62 forks source link

md5로 저장된 낡은 md5 해시된 비번을 pbkdf2(md5_hash)로 저장하기 #2421

Open hackmod opened 4 years ago

hackmod commented 4 years ago

커뮤니티의 낡은 md5 비번은 안전하지 않으며 방통위의 지적사항에 해당됩니다. 모듈을 조금 고쳐서 낡은 md5 해시를 pbkdf2(old_md5_hash)로 고치게끔 해보았습니다.

마땅히 올릴 곳이 없어 이곳에 올리니 필요하신 분 참고해서 고쳐서 쓰시기 바랍니다.

fix_md5.php // xe-core 최상위 디렉토리에 복사하여 $ php fix_md5.php 실행함.

<?php
/**
 * MD5 Fixer by hackyminer@gmail.com
 *
 * @license: MIT
 * @description: fix old MD5 passwords
 */
define('__XE__',   TRUE);
require dirname(__FILE__) . '/config/config.inc.php';

$oContext = Context::getInstance();
$oContext->init();

$oDB = &DB::getInstance();
$query = $oDB->_query("select * from xe_member_expired where password not like 'sha256%'");
//$query = $oDB->_query("select * from xe_member where password not like 'sha256%'");
//$query = $oDB->_query("select * from xe_member_expired where password not like 'sha256%' limit 2");
$result = $oDB->_fetch($query);

$o = new Password();
$algo = 'pbkdf2';

if (!is_array($result)) {
    $tmp = $result;
    $result = array();
    $result[] = $tmp;
}

foreach ($result as $u) {
    $oldhash = $u->password;
    $newhash = $o->createHash($oldhash, $algo);
    echo $u->user_id,"\t",$u->member_srl,"\t",$oldhash,"\t",$newhash,"\t", $u->denied,"\n";
    //continue;

    $member_srl = $u->member_srl;

    // Execute insert or update depending on the value of member_srl
    $args = new stdClass;
    $args->member_srl = $member_srl;
    $args->password = $newhash;
    $args->denied = $u->denied;
    $output = executeQuery('member_expire.updateMemberPassword', $args);
    //$output = executeQuery('member.updateMemberPassword', $args);
    if(!$output->toBool()) {
        echo $output->getMessage();
        echo "Fail to update password for member=",$u->member_srl,"/",$u->user_id,"\n";
        exit(-1);
    }
}

echo "Total ", sizeof($result), " users password info updated\n";

/* EOF */

member_expire 모듈에 남아있는 낡은 md5 해시를 업데이트 하려면 다음과 같은 query xml을 복사해 넣어줍니다. modules/member_expire/queries/updateMemberPassword.xml

<query id="updateMemberPassword" action="update">
    <tables>
        <table name="member_expired" />
    </tables>
    <columns>
        <column name="password" var="password" notnull="notnull" />
        <column name="denied" var="denied" />
    </columns>
    <conditions>
        <condition operation="equal" column="member_srl" var="member_srl" notnull="notnull" filter="number" />
    </conditions>
</query>

마지막으로, 이를 지원하기 위해 고쳐야 할 Password.php 모듈 파일

diff --git a/classes/security/Password.class.php b/classes/security/Password.class.php
index 6ab5947..b354acb 100644
--- a/classes/security/Password.class.php
+++ b/classes/security/Password.class.php
@@ -155,7 +155,20 @@ class Password
                                $hash = explode(':', $hash);
                                $hash[3] = base64_decode($hash[3]);
                                $hash_to_compare = $this->pbkdf2($password, $hash[2], $hash[0], intval($hash[1], 10), strlen($hash[3]));
-                               return $this->strcmpConstantTime($hash_to_compare, $hash[3]);
+                               $check1 = $this->strcmpConstantTime($hash_to_compare, $hash[3]);
+                               if ($check1) return $check1;
+
+                               // saved hash is pbkdf2(md5(password));
+                               $tmp = md5($password);
+                               $hash_to_compare = $this->pbkdf2($tmp, $hash[2], $hash[0], intval($hash[1], 10), strlen($hash[3]));
+                               $check2 = $this->strcmpConstantTime($hash_to_compare, $hash[3]);
+                               if ($check2) return $check2;
+
+                               // saved hash is pbkdf2(md5(sha1(md5(password))));
+                               $tmp = md5(sha1(md5($password)));
+                               $hash_to_compare = $this->pbkdf2($tmp, $hash[2], $hash[0], intval($hash[1], 10), strlen($hash[3]));
+                               $check3 = $this->strcmpConstantTime($hash_to_compare, $hash[3]);
+                               return $check3;

                        case 'bcrypt':
                                $hash_to_compare = $this->bcrypt($password, $hash);