diff --git a/common/framework/session.php b/common/framework/session.php index 44a36d1e7..0b93e299f 100644 --- a/common/framework/session.php +++ b/common/framework/session.php @@ -126,7 +126,7 @@ class Session // Hacked session! Destroy everything. $_SESSION = array(); $must_create = true; - self::setAutologinKeys(null, null); + self::destroyAutologinKeys(); } } else @@ -154,7 +154,7 @@ class Session // Hacked session! Destroy everything. $_SESSION = array(); $must_create = true; - self::setAutologinKeys(null, null); + self::destroyAutologinKeys(); } } @@ -353,11 +353,12 @@ class Session if ($member_srl) { $_SESSION['RHYMIX']['login'] = $_SESSION['member_srl'] = intval($member_srl); + $_SESSION['RHYMIX']['last_login'] = time(); $_SESSION['is_logged'] = true; } else { - self::setAutologinKeys(null, null); + self::destroyAutologinKeys(); } } @@ -440,7 +441,7 @@ class Session self::$_started = false; self::$_member_info = false; self::_setKeys(); - self::setAutologinKeys(null, null); + self::destroyAutologinKeys(); @session_destroy(); return true; } @@ -851,7 +852,7 @@ class Session * @param string $security_key * @return bool */ - public static function setAutologinKeys($autologin_key = null, $security_key = null) + public static function setAutologinKeys($autologin_key, $security_key) { // Get session parameters. list($lifetime, $refresh_interval, $domain, $path) = self::_getParams(); @@ -862,18 +863,59 @@ class Session { setcookie('rx_autologin', $autologin_key . $security_key, $lifetime, $path, $domain, false, true); $_COOKIE['rx_autologin'] = $autologin_key . $security_key; + return true; } else { - if (self::$_autologin_key) - { - executeQuery('member.deleteAutologin', (object)array('autologin_key' => substr(self::$_autologin_key, 0, 24))); - self::$_autologin_key = false; - } - setcookie('rx_autologin', 'deleted', time() - 86400, $path, $domain, false, true); - unset($_COOKIE['rx_autologin']); + return false; + } + } + + /** + * Destroy autologin keys. + * + * @return bool + */ + public static function destroyAutologinKeys() + { + if (self::$_autologin_key) + { + executeQuery('member.deleteAutologin', (object)array('autologin_key' => substr(self::$_autologin_key, 0, 24))); + self::$_autologin_key = false; + $result = true; + } + else + { + $result = false; } + setcookie('rx_autologin', 'deleted', time() - 86400, $path, $domain, false, true); + unset($_COOKIE['rx_autologin']); + return $result; + } + + /** + * Destroy all other autologin keys (except the current session). + * + * @param int $member_srl + * @return bool + */ + public static function destroyOtherAutologinKeys($member_srl) + { + $member_srl = intval($member_srl); + if (!$member_srl) + { + return false; + } + + if (self::$_autologin_key) + { + executeQuery('member.deleteAutologin', (object)array('member_srl' => $member_srl, 'not_autologin_key' => substr(self::$_autologin_key, 0, 24))); + } + else + { + executeQuery('member.deleteAutologin', (object)array('member_srl' => $member_srl)); + } return true; } } diff --git a/modules/member/lang/en.php b/modules/member/lang/en.php index 800144cf2..a13cd0041 100644 --- a/modules/member/lang/en.php +++ b/modules/member/lang/en.php @@ -161,6 +161,7 @@ $lang->cmd_config_password_strength = 'password strength'; $lang->cmd_password_hashing_algorithm = 'Password Hashing Algorithm'; $lang->cmd_password_hashing_work_factor = 'Password Hashing Work Factor'; $lang->cmd_password_hashing_auto_upgrade = 'Auto-upgrade Hashing Algorithm'; +$lang->cmd_password_change_invalidate_other_sessions = 'Log out other devices on password change'; $lang->password_strength_low = 'low'; $lang->password_strength_normal = 'normal'; $lang->password_strength_high = 'high'; @@ -168,6 +169,7 @@ $lang->about_password_strength_config = 'When members register or change the pas $lang->about_password_hashing_algorithm = 'You can choose how to encrypt (hash) members\' passwords stored in the database.'; $lang->about_password_hashing_work_factor = 'Higher work factors are more secure, but logins may take a long time. This only applies to bcrypt and pbkdf2.'; $lang->about_password_hashing_auto_upgrade = 'Passwords encrypted using different algorithms will be automatically converted to the configured algorithm at next login.'; +$lang->about_password_change_invalidate_other_sessions = 'Log out all other devices (browsers) when a member changes the password.'; $lang->about_password_strength['low'] = 'the password must be at least 4'; $lang->about_password_strength['normal'] = 'the password must be at least 6, and must have at least one alpha character and numeric characters'; $lang->about_password_strength['high'] = 'the password must be at least 8, and must have at least one alpha character, numeric character and special character '; diff --git a/modules/member/lang/ko.php b/modules/member/lang/ko.php index f20d1752f..a7b604339 100644 --- a/modules/member/lang/ko.php +++ b/modules/member/lang/ko.php @@ -170,6 +170,7 @@ $lang->cmd_config_password_strength = '비밀번호 보안수준'; $lang->cmd_password_hashing_algorithm = '비밀번호 암호화 알고리듬'; $lang->cmd_password_hashing_work_factor = '비밀번호 암호화 소요시간'; $lang->cmd_password_hashing_auto_upgrade = '알고리듬 자동 업그레이드'; +$lang->cmd_password_change_invalidate_other_sessions = '비번 변경시 다른 기기 로그아웃'; $lang->password_strength_low = '낮음'; $lang->password_strength_normal = '보통'; $lang->password_strength_high = '높음'; @@ -177,6 +178,7 @@ $lang->about_password_strength_config = '회원들이 비밀번호를 등록/변 $lang->about_password_hashing_algorithm = '회원들의 비밀번호를 DB에 저장할 때 암호화(해싱)하는 방식을 지정할 수 있습니다.'; $lang->about_password_hashing_work_factor = '시간이 오래 걸리는 알고리듬일수록 보안이 강하지만, 로그인이 오래 걸릴 수 있습니다. bcrypt 및 pbkdf2 알고리듬에만 적용됩니다.'; $lang->about_password_hashing_auto_upgrade = '설정된 알고리듬과 다른 방법으로 암호화된 비밀번호가 있으면 다음 로그인시 설정된 알고리듬으로 자동 변환합니다.'; +$lang->about_password_change_invalidate_other_sessions = '비밀번호를 변경하면 현재 기기(브라우저)를 제외한 모든 로그인이 풀리도록 합니다.'; $lang->about_password_strength['low'] = '비밀번호는 4자 이상이어야 합니다.'; $lang->about_password_strength['normal'] = '비밀번호는 6자리 이상이어야 하며 영문과 숫자를 반드시 포함해야 합니다.'; $lang->about_password_strength['high'] = '비밀번호는 8자리 이상이어야 하며 영문과 숫자, 특수문자를 반드시 포함해야 합니다.'; diff --git a/modules/member/member.admin.controller.php b/modules/member/member.admin.controller.php index 70ff7e575..e34edaa74 100644 --- a/modules/member/member.admin.controller.php +++ b/modules/member/member.admin.controller.php @@ -171,6 +171,7 @@ class memberAdminController extends member 'password_hashing_algorithm', 'password_hashing_work_factor', 'password_hashing_auto_upgrade', + 'password_change_invalidate_other_sessions', 'update_nickname_log', 'member_allow_fileupload' ); diff --git a/modules/member/member.controller.php b/modules/member/member.controller.php index 1ec17d081..d375844b1 100644 --- a/modules/member/member.controller.php +++ b/modules/member/member.controller.php @@ -735,6 +735,21 @@ class memberController extends member $args->password = $password; $output = $this->updateMemberPassword($args); if(!$output->toBool()) return $output; + + // Log out all other sessions. + $oModuleModel = getModel('module'); + $member_config = $oModuleModel->getModuleConfig('member'); + if ($member_config->password_change_invalidate_other_sessions === 'Y') + { + $invalid_before = time(); + $filename = RX_BASEDIR . sprintf('files/member_extra_info/invalid_before/%s%d.txt', getNumberingPath($member_srl), $member_srl); + Rhymix\Framework\Storage::write($filename, $invalid_before); + Rhymix\Framework\Session::destroyOtherAutologinKeys($member_srl); + if ($_SESSION['RHYMIX'] && $_SESSION['RHYMIX']['last_login']) + { + $_SESSION['RHYMIX']['last_login'] = $invalid_before; + } + } $this->add('member_srl', $args->member_srl); $this->setMessage('success_updated'); @@ -771,6 +786,7 @@ class memberController extends member $output = $this->deleteMember($member_srl); if(!$output->toBool()) return $output; // Destroy all session information + executeQuery('member.deleteAutologin', (object)array('member_srl' => $member_srl)); Rhymix\Framework\Session::logout(); // Return success message $this->setMessage('success_leaved'); @@ -1922,6 +1938,21 @@ class memberController extends member $this->destroySessionInfo(); return; } + + // Invalidate the session if the member's password has changed + $oModuleModel = getModel('module'); + $member_config = $oModuleModel->getModuleConfig('member'); + if ($member_config->password_change_invalidate_other_sessions === 'Y') + { + $filename = RX_BASEDIR . sprintf('files/member_extra_info/invalid_before/%s%d.txt', getNumberingPath($member_srl), $member_srl); + $invalid_before = Rhymix\Framework\Storage::read($filename); + if ($invalid_before && $_SESSION['RHYMIX'] && $_SESSION['RHYMIX']['last_login'] && $_SESSION['RHYMIX']['last_login'] < $invalid_before) + { + $this->destroySessionInfo(); + return; + } + } + // Log in for treatment sessions set /* $_SESSION['is_logged'] = true; diff --git a/modules/member/queries/deleteAutologin.xml b/modules/member/queries/deleteAutologin.xml index ac8ae821b..41aabfeec 100644 --- a/modules/member/queries/deleteAutologin.xml +++ b/modules/member/queries/deleteAutologin.xml @@ -4,6 +4,7 @@ + diff --git a/modules/member/tpl/default_config.html b/modules/member/tpl/default_config.html index f1f95be0a..a796c748e 100644 --- a/modules/member/tpl/default_config.html +++ b/modules/member/tpl/default_config.html @@ -71,6 +71,14 @@

{$lang->about_password_hashing_auto_upgrade}

+
+ +
+ + +

{$lang->about_password_change_invalidate_other_sessions}

+
+