From f8ae6f9885cbf267c948b5d81906081f4f284351 Mon Sep 17 00:00:00 2001 From: misol Date: Mon, 25 Jun 2012 14:49:18 +0000 Subject: [PATCH] Issue 1816 Defence infinite loop of ID/Password insertion test. git-svn-id: http://xe-core.googlecode.com/svn/branches/1.5.0@10793 201d5d3c-b55e-5fd7-737f-ddc643e51545 --- modules/member/lang/lang.xml | 33 +++++ modules/member/member.admin.controller.php | 1 + modules/member/member.class.php | 53 +++++++- modules/member/member.controller.php | 138 +++++++++++++++------ modules/member/member.model.php | 3 + modules/member/ruleset/insertConfig.xml | 2 + modules/member/tpl/member_config.html | 8 ++ 7 files changed, 200 insertions(+), 38 deletions(-) diff --git a/modules/member/lang/lang.xml b/modules/member/lang/lang.xml index fcee32d4e..1d2d8cf8e 100644 --- a/modules/member/lang/lang.xml +++ b/modules/member/lang/lang.xml @@ -155,6 +155,10 @@ + + + + @@ -2234,6 +2238,22 @@ Bạn có thể quản lý thành viên bằng cách tạo những nhóm mới, + + + + + + + + + + + + + + + + @@ -2467,9 +2487,22 @@ Bạn có thể quản lý thành viên bằng cách tạo những nhóm mới, + + + + + + + + + + + 확인하지 않은 로그인 실패 기록이 있습니다.
%1$s

* 이 알림은 한번만 보입니다.
* 이 메시지는 쪽지와 이메일로 발송됩니다.
* 이 메시지는 로그인이 성공한 순간, 로그인 성공 이전 실패 기록을 모아서 발송합니다.
발송 시각: %2$s

]]>
+ There is unfolded sign in failure report
%1$s

* This notification is shown once.
* This message will be send to your email and message.
* This message contains sign in failure records, before a ID sign in success.
Sending: %2$s

]]>
+
diff --git a/modules/member/member.admin.controller.php b/modules/member/member.admin.controller.php index f79d83b74..f7ed1944e 100644 --- a/modules/member/member.admin.controller.php +++ b/modules/member/member.admin.controller.php @@ -133,6 +133,7 @@ 'webmaster_email', 'limit_day', 'change_password_date', + 'max_error_count','max_error_count_time', 'agreement', 'after_login_url', 'after_logout_url', diff --git a/modules/member/member.class.php b/modules/member/member.class.php index ee17290ad..7fa87869a 100644 --- a/modules/member/member.class.php +++ b/modules/member/member.class.php @@ -337,5 +337,56 @@ $store = new Auth_OpenID_XEStore(); $store->reset(); } - } + + /** + * @brief Record login error and return the error, about IPaddress. + **/ + function recordLoginError($error = 0, $message = 'success') + { + if($error == 0) return new Object($error, $message); + + $args->ipaddress = $_SERVER['REMOTE_ADDR']; + + $output = executeQuery('member.getLoginCountByIp', $args); + if($output->data && $output->data->count) + { + //update + $args->count = $output->data->count + 1; + $output = executeQuery('member.updateLoginCountByIp', $args); + } + else + { + //insert + $args->count = 1; + $output = executeQuery('member.insertLoginCountByIp', $args); + } + return new Object($error, $message); + } + + /** + * @brief Record login error and return the error, about MemberSrl. + **/ + function recordMemberLoginError($error = 0, $message = 'success', $args = NULL) + { + if($error == 0 || !$args->member_srl) return new Object($error, $message); + + $output = executeQuery('member.getLoginCountHistoryByMemberSrl', $args); + if($output->data && $output->data->content) + { + //update + $content = unserialize($output->data->content); + $content[] = array($_SERVER['REMOTE_ADDR'],Context::getLang($message),time()); + $args->content = serialize($content); + $output = executeQuery('member.updateLoginCountHistoryByMemberSrl', $args); + } + else + { + //insert + $content[0] = array($_SERVER['REMOTE_ADDR'],Context::getLang($message),time()); + $args->content = serialize($content); + $output = executeQuery('member.insertLoginCountHistoryByMemberSrl', $args); + } + return $this->recordLoginError($error, $message); + } + } ?> diff --git a/modules/member/member.controller.php b/modules/member/member.controller.php index 00f67de00..c1200271c 100644 --- a/modules/member/member.controller.php +++ b/modules/member/member.controller.php @@ -1516,47 +1516,111 @@ } } - /** - * @brief Log-in - **/ - function doLogin($user_id, $password = '', $keep_signed = false) { - $user_id = strtolower($user_id); - // Call a trigger before log-in (before) - $trigger_obj->user_id = $user_id; - $trigger_obj->password = $password; - $trigger_output = ModuleHandler::triggerCall('member.doLogin', 'before', $trigger_obj); - if(!$trigger_output->toBool()) return $trigger_output; - // Create a member model object - $oMemberModel = &getModel('member'); + /** + * @brief Log-in + **/ + function doLogin($user_id, $password = '', $keep_signed = false) { + $user_id = strtolower($user_id); + if(!$user_id) return new Object(-1, 'null_user_id'); + // Call a trigger before log-in (before) + $trigger_obj->user_id = $user_id; + $trigger_obj->password = $password; + $trigger_output = ModuleHandler::triggerCall('member.doLogin', 'before', $trigger_obj); + if(!$trigger_output->toBool()) return $trigger_output; + // Create a member model object + $oMemberModel = &getModel('member'); - // check identifier - $config = $oMemberModel->getMemberConfig(); - if ($config->identifier == 'email_address'){ - // Get user_id information - $this->memberInfo = $oMemberModel->getMemberInfoByEmailAddress($user_id); - // Set an invalid user if no value returned - if(!$user_id || strtolower($this->memberInfo->email_address) != strtolower($user_id)) return new Object(-1, 'invalid_email_address'); + // check IP access count. + $config = $oMemberModel->getMemberConfig(); + $args->ipaddress = $_SERVER['REMOTE_ADDR']; + $output = executeQuery('member.getLoginCountByIp', $args); + $count = (int)$output->data->count; + if($config->max_error_count < $count) + { + $last_update = $output->data->last_update; + $year = substr($last_update,0,4); + $month = substr($last_update,4,2); + $day = substr($last_update,6,2); + $hour = substr($last_update,8,2); + $min = substr($last_update,10,2); + $sec = substr($last_update,12,2); + $last_update = mktime($hour,$min,$sec,$month,$day,$year); + $term = intval(time()-$last_update); + if($term < $config->max_error_count_time) + { + $term = $config->max_error_count_time - $term; + if($term < 60) $term = intval($term).Context::getLang('unit_sec'); + elseif(60 <= $term && $term < 3600) $term = intval($term/60).Context::getLang('unit_min'); + elseif(3600 <= $term && $term < 86400) $term = intval($term/3600).Context::getLang('unit_hour'); + else $term = intval($term/86400).Context::getLang('unit_day'); + return new Object(-1, sprintf(Context::getLang('excess_ip_access_count'),$term)); + } + else + { + $args->ipaddress = $_SERVER['REMOTE_ADDR']; + $output = executeQuery('member.deleteLoginCountByIp', $args); + } + } - }else{ - // Get user_id information - $this->memberInfo = $oMemberModel->getMemberInfoByUserID($user_id); - // Set an invalid user if no value returned - if(!$user_id || strtolower($this->memberInfo->user_id) != strtolower($user_id)) return new Object(-1, 'invalid_user_id'); - } - // Password Check - if($password && !$oMemberModel->isValidPassword($this->memberInfo->password, $password, $this->memberInfo->member_srl)) return new Object(-1, 'invalid_password'); - // If denied == 'Y', notify - if($this->memberInfo->denied == 'Y') { - $args->member_srl = $this->memberInfo->member_srl; - $output = executeQuery('member.chkAuthMail', $args); - if ($output->toBool() && $output->data->count != '0') return new Object(-1,'msg_user_not_confirmed'); - return new Object(-1,'msg_user_denied'); - } - // Notify if denied_date is less than the current time - if($this->memberInfo->limit_date && substr($this->memberInfo->limit_date,0,8) >= date("Ymd")) return new Object(-1,sprintf(Context::getLang('msg_user_limited'),zdate($this->memberInfo->limit_date,"Y-m-d"))); + // check identifier + if ($config->identifier == 'email_address'){ + // Get user_id information + $this->memberInfo = $oMemberModel->getMemberInfoByEmailAddress($user_id); + // Set an invalid user if no value returned + if(!$user_id || strtolower($this->memberInfo->email_address) != strtolower($user_id)) return $this->recordLoginError(-1, 'invalid_email_address'); + + }else{ + // Get user_id information + $this->memberInfo = $oMemberModel->getMemberInfoByUserID($user_id); + // Set an invalid user if no value returned + if(!$user_id || strtolower($this->memberInfo->user_id) != strtolower($user_id)) return $this->recordLoginError(-1, 'invalid_user_id'); + } + // Password Check + if($password && !$oMemberModel->isValidPassword($this->memberInfo->password, $password, $this->memberInfo->member_srl)) return $this->recordMemberLoginError(-1, 'invalid_password',$this->memberInfo); + // If denied == 'Y', notify + if($this->memberInfo->denied == 'Y') { + $args->member_srl = $this->memberInfo->member_srl; + $output = executeQuery('member.chkAuthMail', $args); + if ($output->toBool() && $output->data->count != '0') return new Object(-1,'msg_user_not_confirmed'); + return new Object(-1,'msg_user_denied'); + } + // Notify if denied_date is less than the current time + if($this->memberInfo->limit_date && substr($this->memberInfo->limit_date,0,8) >= date("Ymd")) return new Object(-1,sprintf(Context::getLang('msg_user_limited'),zdate($this->memberInfo->limit_date,"Y-m-d"))); // Update the latest login time $args->member_srl = $this->memberInfo->member_srl; $output = executeQuery('member.updateLastLogin', $args); + // check if there is login fail records. + $output = executeQuery('member.getLoginCountHistoryByMemberSrl', $args); + if($output->data && $output->data->content) + { + $title = Context::getLang('login_fail_report'); + $message = ''; + $content = sprintf(Context::getLang('login_fail_report_contents'),$message,date('Y-m-d H:i:s P')); + + //send message + $oCommunicationController = &getController('communication'); + $oCommunicationController->sendMessage($args->member_srl, $args->member_srl, $title, $content, true); + + if($this->memberInfo->email_address && $this->memberInfo->allow_mailing == 'Y') + { + $view_url = Context::getRequestUri(); + $title = sprintf("%s @ %s",$title,$view_url); + $content = sprintf("%s

From: %s
To: %s(%s)

",$content, $view_url, $view_url, $this->memberInfo->nick_name, $this->memberInfo->email_id); + $oMail = new Mail(); + $oMail->setTitle($title); + $oMail->setContent($content); + $oMail->setSender($this->memberInfo->email_id.'('.$this->memberInfo->nick_name.')', $this->memberInfo->email_address); + $oMail->setReceiptor($this->memberInfo->email_id.'('.$this->memberInfo->nick_name.')', $this->memberInfo->email_address); + $oMail->send(); + } + $output = executeQuery('member.deleteLoginCountHistoryByMemberSrl', $args); + } // Call a trigger after successfully log-in (after) $trigger_output = ModuleHandler::triggerCall('member.doLogin', 'after', $this->memberInfo); if(!$trigger_output->toBool()) return $trigger_output; @@ -1567,7 +1631,7 @@ $autologin_args->member_srl = $this->memberInfo->member_srl; executeQuery('member.deleteAutologin', $autologin_args); $autologin_output = executeQuery('member.insertAutologin', $autologin_args); - if($autologin_output->toBool()) setCookie('xeak',$autologin_args->autologin_key, time()+60*60*24*365, '/'); + if($autologin_output->toBool()) setCookie('xeak',$autologin_args->autologin_key, time()+31536000, '/'); } if($this->memberInfo->is_admin == 'Y') { $oMemberAdminModel = &getAdminModel('member'); diff --git a/modules/member/member.model.php b/modules/member/member.model.php index 837945726..87adbfc50 100644 --- a/modules/member/member.model.php +++ b/modules/member/member.model.php @@ -51,6 +51,9 @@ if (!$config->identifier) $config->identifier = 'user_id'; + if (!$config->max_error_count) $config->max_error_count = 10; + if (!$config->max_error_count_time) $config->max_error_count_time = 300; + return $config; } diff --git a/modules/member/ruleset/insertConfig.xml b/modules/member/ruleset/insertConfig.xml index 58503c1cf..72ad3e29e 100644 --- a/modules/member/ruleset/insertConfig.xml +++ b/modules/member/ruleset/insertConfig.xml @@ -5,5 +5,7 @@ + + diff --git a/modules/member/tpl/member_config.html b/modules/member/tpl/member_config.html index a1663489d..95c947de3 100644 --- a/modules/member/tpl/member_config.html +++ b/modules/member/tpl/member_config.html @@ -51,6 +51,14 @@

{$lang->unit_day}({$lang->about_change_password_date})

+
  • +

    +

    {$lang->about_login_trial_limit1}

    +
  • +
  • +

    + {$lang->unit_sec}({$lang->about_login_trial_limit2})

    +
  • {$editor}