I am trying to implement my own Http Session class but I cannot get it right. The browser only waiting for data for some time and then after some time, it returns “The page cannot be found” as result If I am go back to CHttpSession everything works fine.
I am using the following code and would be glad if someone could help me.
class DbHttpSession extends CHttpSession
{
public $ext_options = array();
public function init()
{
foreach($this->ext_options as $key => $value)
{
$opt = 'session.'.$key;
ini_set($opt,$value);
}
parent::init();
}
public function close()
{
if(!Yii::app()->user->isGuest)
{
$this->regenerateID(true);
}
parent::close();
}
public function getUseCustomStorage()
{
return true;
}
public function regenerateID($deleteOldSession=false)
{
$oldID = session_id();;
parent::regenerateID(false);
$newID = session_id();
$db = $this->getDbConnection();
$sql="CALL session_set_id(unhex('$oldID'),unhex('$newID'))";
$row = $db->createCommand($sql)->execute();
return ($row > 0);
}
protected function getDbConnection()
{
return Yii::app()->db;
}
public function openSession($savePath,$sessionName)
{
return true;
}
public function closeSession()
{
return true;
}
public function readSession($id)
{
try
{
$timeout = (int)$this->getTimeout();
$sql = "CALL session_read(unhex('$id'), $timeout)";
$data = $this->getDbConnection()->createCommand($sql)->queryScalar();
return $data === false ? '' : $data;
}
catch(Exception $exp)
{
return '';
}
}
public function writeSession($id,$data)
{
if(strlen($data) === 0)
{
return true;
}
try
{
$db=$this->getDbConnection();
$sql = "call session_write(unhex(:id), :data)";
$command = $connection->createCommand($sql);
$command->bindParam(":id",$id,PDO::PARAM_STR);
$command->bindParam(":data",$data,PDO::PARAM_STR);
return $this->getDbConnection()->createCommand($sql)->execute() !== 0;
}
catch(Exception $e)
{
if(YII_DEBUG)
{
echo $e->getMessage();
// it is too late to log an error message here
}
return false;
}
}
public function destroySession($id)
{
$sql="CALL session_destroy(unhex('$id'))";
return ($this->getDbConnection()->createCommand($sql)->execute() !== 0);
}
public function gcSession($maxLifetime)
{
$sql="CALL session_gc('$maxLifetime')";
$this->getDbConnection()->createCommand($sql)->execute();
return true;
}
}
I fixed it now, after some sleeping Here is my code, if someone is interested. I also added a security method: abnormalBehaviour() which can be used to check if someone is trying to hijack the session. I will when I got the time ask for improvements in a new thread for it, but for now, here is my code.
class DbHttpSession extends CHttpSession
{
public $ext_options = array();
private $_db = null;
public function init()
{
foreach($this->ext_options as $key => $value)
{
$opt = 'session.'.$key;
ini_set($opt,$value);
}
parent::init();
}
public function abnormalBehaviour()
{
$request = Yii::app()->getComponent('request',true);
// check the users user agent activity and the user ip address. If it suddenly changes should
//this method return true
$user_agent = $request->getUserAgent();
$user_host_address = $request->getUserHostAddress();
if($user_agent == null)
{
$user_agent = 'null';
}
$keyprefix = 'DbHttpSession.HijackCheck';
$init = $this->get($keyprefix.'initialized');
if($init == null || $init !== true)
{
//first run, set start variables
$this->add($keyprefix.'user_agents', array($user_agent => 1));
$this->add($keyprefix.'user_host_addresses', array($user_host_address => 0));
$this->add($keyprefix.'initialized', true);
}
else
{
$user_agents = $this->get($keyprefix.'user_agents');
if(!isset($user_agents[$user_agent]))
{
$user_agents[$user_agent] = 0;
}
++$user_agents[$user_agent];
$total_sum = (float)array_sum($user_agents);
$part_sum = (float)$user_agents[$user_agent];
if($part_sum/$total_sum < 0.07)
{
//we believe one session hijack attempt is catch here.
return true;
}
if(count($user_agents) > 50)
{
//remove the front element, just save the last 50 recently used user agents.
unshift($user_agents);
}
//do the same check but for ip addresses
$user_host_addresses = $this->get($keyprefix.'user_host_address');
if(!isset($user_host_addresses[$user_host_address]))
{
$user_host_addresses[$user_host_address] = 0;
}
++$user_host_addresses[$user_host_address];
$total_sum = (float)array_sum($user_host_addresses);
$part_sum = (float)$user_host_addresses[$user_host_address];
if($part_sum/$total_sum < 0.07)
{
//we believe one session hijack attempt is catch here.
return true;
}
//we could also do loose check, if all ip addresses is in same group and suddenly changes
//to another upper group should we assume hijack attempt, implement it in the future.
if(count($user_host_addresses) > 50)
{
//remove the front element, just save the last 50 recently used user agents.
unshift($user_host_addresses);
}
}
return false;
}
public function getUseCustomStorage()
{
return true;
}
public function regenerateID($deleteOldSession=false)
{
$oldID = session_id();;
parent::regenerateID(false);
$newID = session_id();
$db = $this->getDbConnection();
$sql="CALL session_set_id(unhex(:old),unhex(:new))";
$command = $db->createCommand($sql);
$command->bindParam(":old", $oldID, PDO::PARAM_STR);
$command->bindParam(":new", $newID, PDO::PARAM_STR);
return ($command->execute() !== 0);
}
protected function getDbConnection()
{
if($this->_db !== null)
{
return $this->_db;
}
$this->_db = Yii::app()->getComponent('db',true);
return $this->_db;
}
public function openSession($savePath,$sessionName)
{
return true;
}
public function closeSession()
{
return true;
}
public function readSession($id)
{
$timeout = (int)$this->getTimeout();
$db = $this->getDbConnection();
$sql = "CALL session_read(unhex(:id), :timeout)";
$command = $db->createCommand($sql);
$command->bindParam(":id",$id,PDO::PARAM_STR);
$command->bindParam(":timeout",$timeout,PDO::PARAM_INT);
$data = $command->queryScalar();
return ($data === false) ? '' : $data;
}
public function writeSession($id,$data)
{
if($data == NULL || strlen($data) == 0)
{
return true;
}
try
{
$db = $this->getDbConnection();
$sql = "CALL session_write(unhex(:id), :data)";
$command = $db->createCommand($sql);
$command->bindParam(":id",$id,PDO::PARAM_STR);
$command->bindParam(":data",$data,PDO::PARAM_STR);
return $command->execute() !== 0;
}
catch(Exception $e)
{
if(YII_DEBUG)
{
echo $e->getMessage();
// it is too late to log an error message here
}
return false;
}
}
public function destroySession($id)
{
$sql="CALL session_destroy(unhex(:id))";
$db = $this->getDbConnection();
$command = $db->createCommand($sql);
$command->bindParam(":id",$id,PDO::PARAM_STR);
return ($command->execute() !== 0);
}
public function gcSession($maxLifetime)
{
$sql="CALL session_gc(:timeout)";
$this->getDbConnection()->createCommand($sql)->bindParam(":timeout",$maxLifetime,PDO::PARAM_INT)->execute();
return true;
}
}