我的用户声称突然我的应用程序假设另一个用户

时间:2018-02-09 15:33:53

标签: php codeigniter

这是我面临的问题,我无法弄清楚问题或调试,因为它不会发生在我身上:

我的用户声称,有时他们登录后,在浏览应用时,他们的登录会话会切换到另一个用户。 他们注意到这一点,例如,导航栏中的名称已切换。

我使用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

尽管如此,我检查了数据库并且没有重复的值。

1 个答案:

答案 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。