Jaaaaaaay Jaaaaaaay - 2 months ago 25
PHP Question

CodeIgniter unexpected session rotation

I have a Codeigniter APP runs on LAMP. I have a weird problem that somehow Codeigniter will destroy the session and generate a new session id during AJAX calls. The thing is I've already set

$config['sess_expiration']
to be 0. In session.php, I changed the code in session_update,

function sess_update()
{
if (($this->userdata['last_activity'] + $this->sess_time_to_update) >= $this->now)
{
return;
}

$old_sessid = $this->userdata['session_id'];
// Pass old session id to new session id directly
$new_sessid = $old_sessid;

$this->userdata['last_activity'] = $this->now;

// _set_cookie() will handle this for us if we aren't using database sessions
// by pushing all userdata to the cookie.
$cookie_data = NULL;

// Update the session ID and last_activity field in the DB if needed
if ($this->sess_use_database === TRUE)
{
// set cookie explicitly to only have our session data
$cookie_data = array();
foreach (array('session_id','ip_address','user_agent','last_activity') as $val)
{
$cookie_data[$val] = $this->userdata[$val];
}

$this->CI->db->query($this->CI->db->update_string($this->sess_table_name, array('last_activity' => $this->now, 'session_id' => $new_sessid), array('session_id' => $old_sessid)));
}

// Write the cookie
$this->_set_cookie($cookie_data);
}


No matter it is "session to update" or not, the session ID should always be the same because I just pass the old session ID to new one. Below is my config setting:

$config['sess_cookie_name'] = 'test';
$config['sess_expiration'] = 0;
$config['sess_expire_on_close'] = TRUE;
$config['sess_encrypt_cookie'] = TRUE;
$config['sess_use_database'] = TRUE;
$config['sess_table_name'] = 'ci_sessions_dev';
$config['sess_match_ip'] = FALSE;
$config['sess_match_useragent'] = FALSE;
$config['sess_time_to_update'] = 3; //the problem happens no matter this value is 3 or 30000


And in the log, when the server kick me out after I login in, I can see the log:

DEBUG - 2016-03-06 13:48:17 --> A session cookie was not found.


Sometimes this problem happen, sometimes it works all day. I'm facing this issue in FF, IE, Chrome. Any idea? Thank you in advance

Answer

Found the problem.

I'm using Codeigniter v2. There is a function called "sess_read" reading the session info from the database. And by default, the session constructor will do a sess_gc to clear all session records in the database with a possibility 5%, which means if there are 20 requests, one of them will trigger the function to clear session records.

Just after it triggers "sess_gc", next request will do a sess_read first, and in this "sess_read", it will check the records in the database, but it won't find the record caz sess_gc just deleted all of them. Then the system will think the session is destroyed, it will do a sess_create instead of sess_update, which will mess up the whole thing.

The terrible thing about this is, if a sess_gc is triggered, the system will delete all session records in the database, thus all users are kicked out. Can't believe Codeigniter does this by default.

What I did is to add a delay in the function, the code now is

function _sess_gc()
{
    if ($this->sess_use_database != TRUE)
    {
        return;
    }

    srand(time());
    if ((rand() % 100) < $this->gc_probability)
    {
        $expire = $this->now - $this->sess_expiration;
        $expire = $expire - 60*60*12*1; //Never delete session less than 12 hours old
        $this->CI->db->where("last_activity < {$expire}");
        $this->CI->db->delete($this->sess_table_name);

        log_message('debug', 'Session garbage collection performed.');
    }
}