这是我面临的问题,我无法弄清楚问题或调试,因为它不会发生在我身上:
我的用户声称,有时他们登录后,在浏览应用时,他们的登录会话会切换到另一个用户。 他们注意到这一点,例如,导航栏中的名称已切换。
我使用CodeIgniter作为框架,我在其他应用程序中没有遇到此问题。注意:该应用程序位于子域(somename.mydomain.com)
我的表格(包含重要字段):
Users -> id, email, session_id
Users_auth -> id, user_id, selector, token, session_id, active, date_expire
验证背后的逻辑,以检查用户是否在浏览页面时登录。
public function isUserLoggedIn()
{
// If the session exists, the user is logged in
if($this->CI->session->userdata('user') != null)
return TRUE;
// The cookie must exists
if(get_cookie('data', TRUE) == null)
return FALSE;
$cookie = get_cookie('data', TRUE);
list($selector, $token) = explode('::', $cookie);
if($selector == null || $token == null)
return FALSE;
// The token in the cookie is not encrypted, but it is on the database
$token = hash('sha256', $token);
$listOfAuths = $this->CI->usersauth_model->getAll($selector); // Ordered by id DESC
// If no selector is found then the auth does not exists or is no longer active
if(count($listOfAuths) <= 0)
return FALSE;
$is_auth_valid = TRUE;
$auth_index = 0;
foreach($listOfAuths as $key => $auth)
{
if(hash_equals($auth->token, $token) == FALSE)
{
$is_auth_valid = FALSE;
break;
}
// Validates the date expire of the auth
if(strtotime(date('Y-m-d H:i:s', strtotime($auth->date_expire))) < strtotime(date('Y-m-d H:i:s')))
{
$is_auth_valid = FALSE;
break;
}
$auth_index = $key;
break;
}
if($is_auth_valid == FALSE)
return FALSE;
// Gets the correct index from the authentication
$lastAuth = $listOfAuths[$auth_index];
/*
* If the code reaches here it means:
* 1. The cookie is well set (selector::token)
* 2. The values from cookie (selector and token) are correct
* 3. The authentication is not experied yet
*/
$user = $this->CI->users_model->get($lastAuth->user_id);
if($user == null || $user->active == FALSE)
return FALSE;
/*
* It is only allowed the user to be loggedIn in one place, therefore
* the current session id must be equal to the last session id set on the UsersAuth table.
*/
if($lastAuth->session_id != $user->session_id)
return FALSE;
$this->auth($user);
return TRUE;
}
成功验证用户后应用的逻辑(通过设置cookie,添加到数据库等):
public function auth($user)
{
$selector = $this->utils->random(12);
$token = $this->utils->random(64);
$token_hash = hash('sha256', $token);
$this->CI->session->set_userdata(array
(
'user' => array
(
'id' => $user->id,
'name' => $user->name,
'email' => $user->email
)
));
$cookie = array
(
'name' => 'data',
'value' => $selector . '::' . $token,
'expire' => 3600 * 24 * 30 * 6
);
set_cookie($cookie);
$auth_history['user_id'] = $user->id;
$auth_history['active'] = TRUE;
$auth_history['date_expire'] = date('Y-m-d H:i:s', strtotime('+1 month'));
$auth_history['selector'] = $selector;
$auth_history['token'] = $token_hash;
$auth_history['session_id'] = $this->CI->session->session_id;
/*
* 1. Sets all auths in the database inactive for the user
* 2. Add's one auth that's the only one active
* 3. Updates the user table is the last session_id
*/
$this->CI->usersauth_model->setAllAuthsInactive($user->id);
$this->CI->usersauth_model->add($auth_history);
$this->CI->users_model->edit($user->id, array('session_id' => $auth_history['session_id']));
}
$this->CI
是CodeIgniter的一个实例。
任何人都可以看到任何导致会话可以在用户之间切换的问题吗?
我唯一想到的事实是selector
可能会在数据库中重复,但令牌不会与选择器匹配,因此该函数将检索FALSE
尽管如此,我检查了数据库并且没有重复的值。
答案 0 :(得分:3)
$this->utils->random()
不会创建唯一号码。它会创建一个随机数。由于您只传递第一个参数,因此选择器是0到12之间的随机数,而令牌是0到64之间的随机数。这不到一千种组合。
如果您需要唯一编号,则应使用多种方法之一生成安全随机数。我不是专家,但我认为random_int()
,random_bytes
()或openssl_random_pseudo_bytes()
至少应该更合适。在下面的评论中,每个DFriend Here's a good article for further reading
此外,如果您只想要简单的用户会话,我认为您在这里做得太多了。我通常只是用会话库设置会话。我从未在数据库中手动处理过cookie或记录的会话或cookie。