Add option to invalidate other sessions on password change

Feature request in https://www.xetown.com/lakepark/345786
This commit is contained in:
Kijin Sung 2017-02-09 00:06:32 +09:00
parent bdb10d57c5
commit c7d8d84500
7 changed files with 99 additions and 12 deletions

View file

@ -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;
}
}

View file

@ -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 ';

View file

@ -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자리 이상이어야 하며 영문과 숫자, 특수문자를 반드시 포함해야 합니다.';

View file

@ -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'
);

View file

@ -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;

View file

@ -4,6 +4,7 @@
</tables>
<conditions>
<condition operation="equal" column="autologin_key" var="autologin_key" />
<condition operation="notequal" column="autologin_key" var="not_autologin_key" pipe="and" />
<condition operation="equal" column="member_srl" var="member_srl" pipe="and" />
</conditions>
</query>

View file

@ -71,6 +71,14 @@
<p class="x_help-block">{$lang->about_password_hashing_auto_upgrade}</p>
</div>
</div>
<div class="x_control-group">
<label class="x_control-label">{$lang->cmd_password_change_invalidate_other_sessions}</label>
<div class="x_controls">
<label for="password_change_invalidate_other_sessions_y" class="x_inline"><input type="radio" name="password_change_invalidate_other_sessions" id="password_change_invalidate_other_sessions_y" value="Y" checked="checked"|cond="$config->password_change_invalidate_other_sessions == 'Y'" /> {$lang->cmd_yes}</label>
<label for="password_change_invalidate_other_sessions_n" class="x_inline"><input type="radio" name="password_change_invalidate_other_sessions" id="password_change_invalidate_other_sessions_n" value="N" checked="checked"|cond="$config->password_change_invalidate_other_sessions != 'Y'" /> {$lang->cmd_no}</label>
<p class="x_help-block">{$lang->about_password_change_invalidate_other_sessions}</p>
</div>
</div>
<div class="x_control-group">
<label class="x_control-label" for="webmaster_name">{$lang->webmaster_name}</label>
<div class="x_controls">