Talking about sessions
Lets talk a minute about a common method of authentication tracking for web applications. Sessions. A session is a server side file, database and/or variable management which a developer can utilize to ensure a user has authenticated and remains a valid authenticated user. I am not going to talk about the dynamics or the best option to utilize here as it is beyond the scope of this document but I do recommend doing your research before starting a project as there are pro's and con's to each method of session management.
Here we are going to present an method of utilizing sessions and storage within a MySQL database. The reason this we decided to forgo the performance boosts of utilizing the mcache or default file based session management is due to our web hosting provider utilizing a shared directory where file based session management gets intermingled with other users, and they do not support the mcache daemon.
Below is the database structure for our database.
Session management table
CREATE TABLE `sessions` ( `session_id` varchar(32) NOT NULL default '', `http_user_agent` varchar(32) NOT NULL default '', `session_data` blob NOT NULL, `session_expire` int(11) NOT NULL default '0', UNIQUE KEY `session_id` (`session_id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1;
Here I am going to provide the PHP class we utilize for our database connections, queries resulting query functions etc.
Database class
class database { var $db; var $server; var $username; var $password; var $database; var $query; var $rows; var $array; var $num_rows; var $affected_rows; var $error; var $errno; function dbConnect( $server, $username, $password, $database ) { $data->db = @mysql_pconnect( $server, $username, $password ); if( !$data->db ) { $data->db = -1; } else { if( !@mysql_select_db( $database ) ) { $data->db = -1; } } return $data->db; } function dbConnectOnly( $server, $username, $password ) { $data->db = @mysql_pconnect( $server, $username, $password ); if( !$data->db ) { $data->db = -1; } else { $data->db = 0; } return $data->db; } function dbQuery( $query, $db ) { $data->query = @mysql_query( $query, $db ); if( !$data->query ) { $data->query = -1; } return $data->query; } function dbNumRows( $id ) { $data->num_rows = @mysql_num_rows( $id ); if( !$data->num_rows === 0 ) { $data->num_rows = -1; } return $data->num_rows; } function dbNumRowsAffected( $id ) { $data->affected_rows = @mysql_affected_rows( $id ) if( !$data->affected_rows === 0 ) { $data->affected_rows = -1; } return $data->affected_rows; } function dbArrayResults( $sql ) { $data->array = array(); while( $rows = @mysql_fetch_array( $sql, MYSQL_ASSOC ) ) { if( !$rows ) { $data->array = -1; } array_push( $data->array, $rows ); } return $data->array; } function dbArrayResultsAssoc( $sql ) { $data->array = array(); while( $rows = @mysql_fetch_assoc( $sql ) ) { if( !$rows ) { $data->array = -1; } array_push( $data->array, $rows ); } return $data->array; } function dbAffectedRows( $sql ) { $rows = @mysql_affected_rows( $sql ); if( $rows === 0 ) { $data->array = -1; } $data->array = 0; return $data->array; } function dbCatchErrno() { return @mysql_errno(); } function dbCatchError() { return @mysql_error(); } function dbFreeData( $sql ) { return @mysql_free_result( $sql ); } function dbCloseConn( $sql ) { return @mysql_close( $sql ); } function dbFixTable( $table, $db ) { @mysql_query( "REPAIR TABLE `" . $table . "`", $db ); @mysql_query( "OPTIMIZE TABLE `" . $table . "`", $db ); @mysql_query( "FLUSH TABLE `" . $table . "`", $db ); } }
And finally our PHP session handler class. As you can see in the '__construct' function we utilize the 'session_set_save_handler' to modify the default PHP subroutine of using flat file's to store and retrieve our global session variables.
Session management class
class sessions { var $id; var $data; var $max_time; var $dbconn; function __construct( $max_time ) { global $defined; if( !empty( $max_time ) ) { @ini_set( 'session.gc_maxlifetime', $max_time ); } else { @ini_set( 'session.gc_maxlifetime', 3600 ); } session_set_save_handler( array( &$this, 'open' ), array( &$this, 'close' ), array( &$this, 'read' ), array( &$this, 'write' ), array( &$this, 'destroy' ), array( &$this, 'gc' ) ); @ini_set( 'cache_limiter', 'private' ); @ini_set( 'cache_expire', $max_time ); @ini_set( 'use_cookies', "1" ); @register_shutdown_function( 'session_write_close' ); @session_start(); } function register( $name, $data ) { return $_SESSION[$name] = $data; } function regen( $flag = false ) { if( $flag !== false ) { $this->register( 'id', session_id() ); @session_regenerate_id( $flag ); $this->id = session_id(); $this->destroy( $_SESSION['id'] ); } return; } function reindex() { global $handles; return $handles['db']->dbFixTable( "sessions", $this->dbconn ); } function open( $path, $name ) { global $defined; global $handles; if( ( $this->dbconn = $handles['db']->dbConnect( $defined['dbhost'], $defined['username'], $defined['password'], $defined['dbname'] ) ) !== -1 ) { return true; } else { return false; } } function close() { global $defined; global $handles; $this->reindex(); $handles['db']->dbFreeData( $this->dbconn ); $handles['db']->dbCloseConn( $this->dbconn ); return true; } function read( $id ) { global $handles; $query = "SELECT * FROM `sessions` WHERE `session_id` = \"" . $id . "\" AND `http_user_agent` = \"" . md5( $_SERVER["HTTP_USER_AGENT"] ) . "\" LIMIT 1"; $result = $handles['db']->dbQuery( $handles['val']->ValidateSQL( $query, $this->dbconn ), $this->dbconn ); if( ( is_resource( $result ) ) && ( $handles['db']->dbNumRowsAffected( $this->dbconn ) > 0 ) ) { $fields = $handles['db']->dbArrayResultsAssoc( $result ); return stripslashes( unserialize( $fields[0]['session_data'] ) ); } return ""; } function write( $id, $data ) { global $handles; $query = "INSERT INTO `sessions` ( `session_id`, `http_user_agent`, `session_data`, `session_expire` ) VALUES ( \"" . $id . "\", \"" . md5( $_SERVER["HTTP_USER_AGENT"] ) . "\", \"" . mysql_real_escape_string( serialize( $data ) ) . "\", \"" . time() . "\" ) ON DUPLICATE KEY UPDATE `session_id` = \"" . $id . "\", `session_data` = \"" . mysql_real_escape_string( serialize( $data ) ) . "\", `session_expire` = \"" . time() . "\""; $result = $handles['db']->dbQuery( $handles['val']->ValidateSQL( $query, $this->dbconn ), $this->dbconn ); if( ( is_resource( $result ) ) && ( $handles['db']->dbNumRowsAffected( $this->dbconn ) > 0 ) ) { $this->reindex(); return true; } else { return false; } } function destroy( $id ) { global $handles; $query = "DELETE FROM `sessions` WHERE `session_id` = \"" . $id . "\" LIMIT 1"; $result = $handles['db']->dbQuery($handles['val']->ValidateSQL( $query, $this->dbconn ), $this->dbconn); if( ( is_resource( $result ) ) && ( $handles['db']->dbNumRowsAffected( $this->dbconn ) > 0 ) ) { $this->reindex(); return true; } else { return false; } } function gc( $max_time ) { global $handles; $query = "DELETE FROM `sessions` WHERE `session_expire` > \"" . time() - $this->max_time . "\""; $result = $handles['db']->dbQuery($handles['val']->ValidateSQL($query, $this->dbconn), $this->dbconn); if( ( is_resource( $result ) ) && ( $handles['db']->dbNumRowsAffected( $this->dbconn ) !== -1 ) ) { $this->reindex(); return true; } return false; } }
Some reasons to utilize a global session variables between your web pages could be to keep a persistent theme, or a specified region code or even to register an authenticated token which will allow your users access to register only data.
Here is a bit of code you can include on your pages to utilize the above classes.
// include the two classes we gave above include 'class.database.php'; include 'class.sessions.php'; // register a variable as a handle of our database class if(!is_resource($handles['dbconn'])) { $handles['dbconn'] = new database; } // check to see if we have an existing session if((!is_resource($handles['session'])&&(!isset($_SESSION))) { $handles['session'] = new sessions; } // now register any variable you wish to persist between pages $_SESSION['variable'] = 'we want this to follow our user from page to page';
And that is it for being able to change PHP's default session save handler from a flat file database to a MySQL database. I also want to mention one thing about the security of using sessions for sensitive data. Having worked with various companies one thing has become blazingly apparent in regards to an attack that first started back in the late 1990's regarding PHP's 'session_id()'. Change it every page your user visits to prevent a common attack known as 'session fixation'. This is very important if you are using server side session variables (whether it be a flat file session handler, a MySQL session handler or even an mcache session handler.
Here is an example to get you started:
// include the two classes we gave above include 'class.database.php'; include 'class.sessions.php'; // register a variable as a handle of our database class if(!is_resource($handles['dbconn'])) { $handles['dbconn'] = new database; } // check to see if we have an existing session if((!is_resource($handles['session'])&&(!isset($_SESSION))) { $handles['session'] = new sessions; } // now register any variable you wish to persist between pages $_SESSION['variable'] = 'we want this to follow our user from page to page'; // prevent session fixation attacks by changing the session_id() $handles['session']->regen();
0 comments:
Post a Comment