Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Store Multi-Session flag in database

Using native PHP session to store the multi-session "prevent_update"
flag caused problems on sites that shared a server with other sites or
contained other CI sites with different configurations or that did not
use sessions.

Corrected some issues with the native PHP sessions not being closed at
the proper time, which prevented other requests from being processed
until the entire request had completed.

Added updates to the sess_write method to prevent expired
multi-sessions from writing user data and setting and updated cookie
which would overwrite the current session id.
  • Loading branch information...
commit 0a5a37fc9cd02b53b3aa8ad249af57a684faa5aa 1 parent 134c4fe
@Areson authored
Showing with 58 additions and 21 deletions.
  1. +58 −21 system/libraries/Session.php
View
79 system/libraries/Session.php
@@ -212,6 +212,13 @@ public function sess_read()
// Is there a corresponding session in the DB?
if ($this->sess_use_database === TRUE)
{
+ // Are we using multi-sessions? If so, grab a lock on the session
+ if ($this->sess_use_multisessions)
+ {
+ // Load the php session based on the current session id.
+ $this->_get_multi_session($session['session_id']);
+ }
+
$this->CI->db->where('session_id', $session['session_id']);
if ($this->sess_match_ip == TRUE)
@@ -230,6 +237,13 @@ public function sess_read()
if ($query->num_rows() === 0)
{
$this->sess_destroy();
+
+ //Kill the multi-session we started
+ if ($this->sess_use_multisessions)
+ {
+ session_destroy();
+ }
+
return FALSE;
}
@@ -248,21 +262,16 @@ public function sess_read()
}
}
- /* Are we in a multi-session scenario? If so, set whether the current
- * session id is allowed to be updated.
- */
+ // Are we in a multi-session scenario? If so, set whether the current
+ // session id is allowed to be updated.
if ($this->sess_use_multisessions)
{
- // Load the php session based on the current session id.
- $this->_get_multi_session($session['session_id']);
-
- $this->prevent_update = isset($_SESSION['prevent_update'])?$_SESSION['prevent_update']:0;
-
- /* Check to see if this session doesn't exist (previously destroyed)
- * or if this session is no longer allowed to update and has exired.
- * If so, kill it.
- */
- if (!isset($_SESSION['prevent_update']) || ($this->prevent_update && ($session['last_activity'] + $this->sess_expiration + $this->sess_multisession_expiration) < $this->now))
+ $this->prevent_update = isset($row->prevent_update)?$row->prevent_update:NULL;
+
+ // Check to see if this session doesn't exist (previously destroyed)
+ // or if this session is no longer allowed to update and has exired.
+ // If so, kill it.
+ if (is_null($this->prevent_update) || ($this->prevent_update && ($row->last_activity + $this->sess_multisession_expiration) < $this->now))
{
$this->sess_destroy();
@@ -296,6 +305,13 @@ public function sess_write()
return;
}
+ // If we have enabled multi-session and have one that
+ // can no longer be updated, prevent the session write.
+ if($this->sess_use_multisessions && $this->prevent_update)
+ {
+ return;
+ }
+
// set the custom userdata, the session data we will set in a second
$custom_userdata = $this->userdata;
$cookie_userdata = array();
@@ -355,6 +371,7 @@ public function sess_create()
'ip_address' => $this->CI->input->ip_address(),
'user_agent' => substr($this->CI->input->user_agent(), 0, 120),
'last_activity' => $this->now,
+ 'prevent_update' => 0,
'user_data' => ''
);
@@ -370,8 +387,11 @@ public function sess_create()
* or not the session can be updated
*/
$this->_get_multi_session($this->userdata['session_id']);
- $_SESSION['prevent_update'] = 0;
- session_write_close();
+ $this->prevent_update = FALSE;
+
+ unset($this->userdata['prevent_update']);
+
+ session_write_close();
}
}
@@ -391,12 +411,18 @@ public function sess_update()
// We only update the session every five minutes by default
if (($this->userdata['last_activity'] + $this->sess_time_to_update) >= $this->now)
{
+ if ($this->sess_use_database && $this->sess_use_multisessions)
+ {
+ session_write_close();
+ }
+
return;
}
// We only allow sessions to update if they are allowed
if ($this->sess_use_database && $this->sess_use_multisessions && $this->prevent_update)
{
+ session_write_close();
return;
}
@@ -457,12 +483,15 @@ public function sess_update()
$cookie_data[$val] = $this->userdata[$val];
}
-
//Are we allowing multiple sessions?
if ($this->sess_use_multisessions)
{
//Set the session as no longer allowing updates
- $_SESSION['prevent_update'] = 1;
+ $this->prevent_update = TRUE;
+
+ //Update the current session
+ $this->CI->db->query($this->CI->db->update_string($this->sess_table_name, array('last_activity' => $this->now, 'prevent_update' => 1), array('session_id' => $old_sessid)));
+
//Release the session lock so other requests can process
session_write_close();
@@ -470,10 +499,10 @@ public function sess_update()
* session id that can continue to update.
*/
$this->_get_multi_session($new_sessid);
- $_SESSION['prevent_update'] = 0;
+ $this->prevent_update = FALSE;
//Write the new session id to the database
- $this->CI->db->query($this->CI->db->insert_string($this->sess_table_name, $cookie_data));
+ $this->CI->db->query($this->CI->db->insert_string($this->sess_table_name, $cookie_data + array('prevent_update' => 0)));
//Make sure the user data is copied over
$this->sess_write();
@@ -881,6 +910,16 @@ protected function _sess_gc()
$this->CI->db->where('last_activity < '.$expire);
$this->CI->db->delete($this->sess_table_name);
+ // Clean up old multi-sessions if they are enabled
+ if($this->sess_use_multisessions)
+ {
+ $expire = $this->now - $this->sess_multisession_expiration;
+
+ $this->CI->db->where('last_activity < '.$expire);
+ $this->CI->db->where('prevent_update = 1');
+ $this->CI->db->delete($this->sess_table_name);
+ }
+
log_message('debug', 'Session garbage collection performed.');
}
}
@@ -907,8 +946,6 @@ protected function _get_multi_session($session_id)
//Don't allow cookies for the php session
ini_set('session.use_cookies', '0');
ini_set('session.use_only_cookies', '0');
- //Make sure that we clean up old sessions in a timely fashion
- ini_set('session.gc_maxlifetime', ($this->sess_multisession_expiration + 10));
//Start a session using our internally generated session id
session_id($session_id);

1 comment on commit 0a5a37f

@Areson
Owner

These changes will require the addition of a "prevent_update" column to the session table. You can use the following table definition as a reference:

CREATE TABLE IF NOT EXISTS  `ci_sessions` (
    session_id varchar(40) DEFAULT '0' NOT NULL,
    ip_address varchar(45) DEFAULT '0' NOT NULL,
    user_agent varchar(120) NOT NULL,
    last_activity int(10) unsigned DEFAULT 0 NOT NULL,
    user_data text NOT NULL,
    prevent_update int(10) DEFAULT NULL,
    PRIMARY KEY (session_id),
    KEY `last_activity_idx` (`last_activity`)
);

Other notable changes:

  • Expired multi-session can no longer write to the database or set an updated cookie. This is to prevent the current session id from being overwritten by a cookie with an old session id.
  • A long-running session will no longer prevent other requests from being processed until it finishes
  • The session garbage collection was updated to cleanup old multi-sessions
Please sign in to comment.
Something went wrong with that request. Please try again.