Hello,
Does anyone know some good literature or has some suggestions how to securely handle $_SESSION vars? By secure I mean stop users from viewing or altering the content of these vars and stop xss/csrf attacks. The reason why I am interested in this topic is because I need to pass a lot of sensitive info from page to page and I believe $_SESSION is more secure then $_GET and/or $_POST. Or is there another way to manipulate vars safer?
Another thing that concerns me is the use of tokens when navigating from page to page.
Thanks.
$_SESSION Security
Moderator: General Moderators
-
andrei.mita
- Forum Commoner
- Posts: 65
- Joined: Sun May 08, 2005 4:06 am
- Location: Barlad/Romania
- andyhoneycutt
- Forum Contributor
- Posts: 468
- Joined: Wed Aug 27, 2008 10:02 am
- Location: Idaho Falls
Re: $_SESSION Security
One thing I do to help in preventing these types of attacks is to store the session in a database. I am careful to regenerate the session on access level changes. Another step I take to help in prevention is to compare the IP address of the current request to the IP address initially stored when the user first was assigned a session.
Although it's not a huge step, more obfuscation than anything, the objects I store in the session are serialized and base64 encoded before I store them as well.
I know there are a few good articles out there that cover this topic very well, and if I happen upon anything like that today, I'll be sure to post here for ya.
-Andy
Although it's not a huge step, more obfuscation than anything, the objects I store in the session are serialized and base64 encoded before I store them as well.
I know there are a few good articles out there that cover this topic very well, and if I happen upon anything like that today, I'll be sure to post here for ya.
-Andy
-
andrei.mita
- Forum Commoner
- Posts: 65
- Joined: Sun May 08, 2005 4:06 am
- Location: Barlad/Romania
Re: $_SESSION Security
If you have some time, could you tell me a little more about your method?
- andyhoneycutt
- Forum Contributor
- Posts: 468
- Joined: Wed Aug 27, 2008 10:02 am
- Location: Idaho Falls
Re: $_SESSION Security
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...
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);
}
}
}- andyhoneycutt
- Forum Contributor
- Posts: 468
- Joined: Wed Aug 27, 2008 10:02 am
- Location: Idaho Falls
Re: $_SESSION Security
This may be a bit overboard at first when you're asking for a more thorough explanation of my method(s), however I feel it's a good starting point to discuss the merit of approaching session security in this manner, as well as gauging how much information you need to be able to handle this task on your own.
-Andy
-Andy