My apologies for the very late response! I've been quite busy the past couple of months and been unable to spend any time here. Here is a class (use at your own risk) that I developed for use on one of my personal sites which allows me to store sessions to database rather than in the filesystem directly. This is one of the steps I use to prevent session hijacking. I'm posting below the primary class here (class.phpsession.php) in conjunction with the other classes which it depends upon to function properly. Minor portions of the LogPDO class were developed by help of an example I found on the web of what others were doing with this concept. If I am able to find the original source I followed to develop this, I will cite it here on this post.
As a side-note, feel free to use this however you wish. There may be redundancies in the code as I have not gone back through this for refactoring purposes. Keep in mind that, as a result of this, some parts may not be very efficient, and may well be bad practice. I submit no claims that this code is in any way production worthy. Also, if you need table explanations in order to use this, feel free to ask...
Code: Select all
<?php
class PHPSession
{
private $pdo;
public function __construct($args = array())
{
session_set_save_handler(
array(&$this, 'open' ),
array(&$this, 'close' ),
array(&$this, 'read' ),
array(&$this, 'write' ),
array(&$this, 'destroy'),
array(&$this, 'gc' )
);
session_start();
}
public function open($save_path, $session_name)
{
try
{
$this->pdo = new LogPDO(App::DB_DSN,App::DB_USER,App::DB_PASS);
}
catch (PDOException $e)
{
$m = "PDOException:\t[{$e->getLine()}]\t {$e->getFile()}\n" .
$e->getTraceAsString();
Errors::Log($m);
return Errors::PDO_CONNECTION;
}
return true;
}
public function close()
{
$this->gc(App::APP_SESSION_LIFE);
$this->pdo = null;
return true;
}
public function read($session_id)
{
$query = "
SELECT session_data, ip_address
FROM " . App::TABLE_SESSION . "
WHERE session_id = :session_id
";
$q = $this->pdo->prepare($query);
$q->execute(array(":session_id" => $session_id));
$r = $q->fetchAll();
if( $r[0]['ip_address'] != $_SERVER['REMOTE_ADDR']
&& !empty($_SESSION) )
{
session_destroy();
return null;
}
return base64_decode($r[0]['session_data']);
}
public function write($session_id, $session_data)
{
if( empty($session_data) || empty($session_id) )
return false;
$expires = time() + App::APP_SESSION_LIFE;
$session_data = base64_encode($session_data);
$query = "
INSERT INTO " . App::TABLE_SESSION . " (
session_id, date_created, last_updated, session_data, expires,
ip_address
) VALUES (
:session_id, NOW(), NOW(), :session_data, :expires, :ip_address
)
ON DUPLICATE KEY UPDATE
session_data = :session_data, last_updated = NOW(),
expires = :expires, ip_address = :ip_address
";
$q = $this->pdo->prepare($query);
$q->execute(array(":session_id" => $session_id,
":session_data" => $session_data,
":expires" => $expires,
":ip_address" => $_SERVER['REMOTE_ADDR']));
return true;
}
public function destroy($session_id)
{
$query = "
DELETE FROM " . App::TABLE_SESSION . "
WHERE session_id = :session_id
";
$q = $this->pdo->prepare($query);
$q->execute(array(":session_id" => $session_id));
return true;
}
public function gc($expires)
{
$query = "DELETE FROM session_data WHERE expires < :expires";
$q = $this->pdo->prepare($query);
$q->execute(array(":expires" => $expires));
return true;
}
}
class App
{
const DB_DSN = "mysql:host=some.hostname.whatever;dbname=nameofdatabase;";
const DB_USER = "databaseusername";
const DB_PASS = "databasepassword";
const TABLE_DB_LOG = "nameofcatalog";
const APP_LOG_PDO = true;
const APP_LOG_DB_TO_FILE = true;
const APP_DB_LOG = "/path/to/query/logfile.txt";
public static function Log($error,$log)
{
if($fp = fopen($log,"a"))
{
fputs($fp,date("Y-m-d H:i:s") . "\t" . $error . "\n");
fclose($fp);
}
}
}
class LogPDO extends PDO
{
public static $log = array();
private $bLog = App::APP_LOG_PDO;
private $bTransaction = false;
public function __construct($dsn, $username = null, $password = null)
{
parent::__construct($dsn, $username, $password);
$this->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE,PDO::FETCH_ASSOC);
}
public function __destruct()
{
if( $this->bLog == true )
{
self::Save();
}
}
public function query($query)
{
$start = microtime();
$result = parent::query($query);
$time = microtime() - $start;
self::$log[] = array(":time" => $time,
":query" => preg_replace('/\s\s+/', ' ', $query),
":ip" => $_SERVER['REMOTE_ADDR']);
return $result;
}
public function prepare($query)
{
return new LogPDOStatement(parent::prepare($query));
}
public function Save()
{
$p = new PDO(App::DB_DSN,App::DB_USER,App::DB_PASS);
foreach(LogPDO::$log as $logitem)
{
$query = "
INSERT INTO " . App::TABLE_DB_LOG . " (
execution_time, query, ip_address, data
)
VALUES (
:time, :query, :ip, :data
)
";
$q = $p->prepare($query);
$q->execute($logitem);
if( App::APP_LOG_DB_TO_FILE )
{
$m = sprintf( "Time: %12s\nIP: %12s\nQuery:%s\nData:%s\n",
$logitem["time"],
$logitem["ip"],
$logitem["query"],
$logitem["data"]
);
App::Log($m,App::APP_DB_LOG);
}
}
$p = null;
}
public function beginTransaction()
{
if ( $this->bTransaction )
return false;
$this->bTransaction = parent::beginTransaction();
return $this->bTransaction;
}
public function commit()
{
$b = parent::commit();
$this->bTransaction = false;
return $b;
}
public function rollback()
{
$b = parent::rollback();
$this->bTransaction = false;
return $b;
}
public function bLog($b = false) { $this->bLog = $b; }
}
class LogPDOStatement
{
private $statement;
public function __construct(PDOStatement $statement)
{
$this->statement = $statement;
}
public function execute($args = array())
{
$start = microtime();
try
{
$result = $this->statement->execute($args);
}
catch(PDOException $e)
{
print_r($e);
exit;
}
$time = microtime() - $start;
$data = serialize($args) . "\n";
LogPDO::$log[] = array("time" => $time,
"query" =>
preg_replace('/\s\s+/', ' ',
$this->statement->queryString),
"ip" => $_SERVER['REMOTE_ADDR'],
"data" => $data
);
return $result;
}
public function __call($function, $args)
{
return call_user_func_array(array($this->statement, $function), $args);
}
}
class Errors
{
const PDO_CONNECTION = 1;
public static function Log($error)
{
if($fp = fopen(App::APP_ERROR_LOG,"a"))
{
fputs($fp,date("Y-m-d H:i:s") . "\t" . $error . "\n");
fclose($fp);
}
}
}