/SPF_Session.php
PHP | 336 lines | 106 code | 60 blank | 170 comment | 13 complexity | 77f1fb6240e5afc46e0bb739a69b8075 MD5 | raw file
- <?php
- /**
- * SPF_Session
- * @package spf
- * @author Simon Downes <simon@simondownes.co.uk>
- * @copyright Copyright (c) 2007, Simon Downes
- * @license http://www.opensource.org/licenses/mit-license.php
- */
-
- /**
- * Base session management class.
- * As a basic session hijacking prevention technique, the $_SESSION['HTTP_USER_AGENT']
- * variable must remain constant between requests using the same session id.
- *
- * TODO: split functionality out into native and database drivers.
- *
- * @final
- * @package spf
- * @author Simon Downes <simon@simondownes.co.uk>
- * @copyright Copyright (c) 2007, Simon Downes
- * @license http://www.opensource.org/licenses/mit-license.php
- */
- final class SPF_Session extends SPF_Object {
-
- /**
- * The session id assigned by PHP (usually a 32 character alpha-numeric string).
- * @var string
- */
- public $session_id;
-
- /**
- * The name of the session.
- * This is the name of the cookie on client machines used to hold the session id.
- * @var string
- */
- public $session_name;
-
- /**
- * Maximum number of seconds allowed between requests using the same session.
- * @var integer
- */
- public $lifetime;
-
- /**
- * Constructor.
- * Session is initialised automatically upon creation of the object.
- *
- * @param string $name name of the cookie used to store the session id on the client.
- * @param integer $lifetime maximum number of seconds allowed between requests using the same session.
- * @return void
- */
- public function __construct( $name = '', $lifetime = 0 ) {
-
- parent::__construct();
-
- // initialise the session
- $this->session_name = $name ? $name : 'SPF_SESSION';
- $this->lifetime = (int) $lifetime;
-
- $this->start();
-
- } // __construct
-
- /**
- * Destructor.
- *
- * @return void
- */
- public function __destruct() {
- // write and pending data to storage
- session_write_close();
- parent::__destruct();
- }
-
- /**
- * Disable PHP5's cloning method so people can't make copies of the session instance.
- *
- * @return void
- */
- public function __clone() {
- throw new SPF_Session_Exception('Object cloning is not allowed for '. __CLASS__);
- }
-
- /**
- * Stores a name/value pair in the session.
- *
- * Using PHP5's overloading for setting and getting variables we can
- * use $session->var = $val and have it stored in the $_SESSION
- * variable. To set an email address, for instance you would do the
- * following:
- *
- * <code>
- * $session->email = 'user@example.com';
- * </code>
- *
- * This doesn't actually store 'user@example.com' into $session->email,
- * rather it is stored in $_SESSION['email'].
- *
- * @param string $var the name of the variable to be stored.
- * @param mixed $val the value to be stored.
- * @return void
- * @link http://us3.php.net/manual/en/language.oop5.overloading.php
- */
- public function __set( $var, $val ) {
- $_SESSION[$var] = $val;
- }
-
- /**
- * Returns the requested session variable.
- *
- * @param string $var the name of the variable to be retrieved.
- * @return mixed the value of the request variable.
- */
- public function __get( $var ) {
- return isset($_SESSION[$var]) ? $_SESSION[$var] : NULL;
- }
-
- /**
- * Determines if a variable exists in the session.
- *
- * @param string $var the name of the variable to look for.
- * @return boolean
- */
- public function __isset( $var ) {
- return isset($_SESSION[$var]);
- }
-
- /**
- * Removes a variable from the session.
- *
- * @param string $var the name of the variable to remove.
- * @return void
- */
- public function __unset( $var ) {
- unset($_SESSION[$var]);
- }
-
- /**
- * Determines if there is an active session.
- *
- * @return boolean
- */
- public function is_active() {
-
- return ($this->session_id !== NULL && strlen($this->session_id) == 32 );
-
- } // is_active
-
- /**
- * Initialises the session.
- *
- * @return void
- */
- public function start() {
-
- // if there is already an active session then do nothing
- if( $this->is_active() )
- return;
-
- // set the session name and load the session data
- session_name( $this->session_name );
- session_start();
-
- // store session parameters
- $this->session_name = session_name();
- $this->session_id = session_id();
-
- // if we have previously stored the USER_AGENT header then check that the
- // current header matches the stored one, and if not then throw and exception
- // as someone is probably attempting to hijack the session...
- if( isset($_SESSION['HTTP_USER_AGENT']) && $_SESSION['HTTP_USER_AGENT'] != md5($_SERVER['HTTP_USER_AGENT']) ) {
- $this->destroy();
- throw new SPF_Session_Exception('Attempt to hijack a session!');
- }
- // if no USER_AGENT header is currently stored then store it
- else
- $_SESSION['HTTP_USER_AGENT'] = md5($_SERVER['HTTP_USER_AGENT']);
-
- // check that the secure session token is valid if it exists
- if( !$this->check_token() ) {
- $this->destroy();
- throw new SPF_Session_Exception('Invalid Token');
- }
-
- // if session lifetime is enabled...
- if( $this->lifetime > 0 ) {
-
- // if expiry is set and less than the current timestamp then session has expired...
- if( isset($_SESSION['session_expires']) && $_SESSION['session_expires'] < time() ) {
- $this->destroy();
- throw new SPF_Session_Exception('Expired');
- }
- // otherwise we just need to (re)set the expiry timestamp
- else
- $_SESSION['session_expires'] = time() + ($this->lifetime * 60);
-
- }
-
-
- } // start
-
- /**
- * Clears the session data but keeps the session active.
- *
- * @return void
- */
- public function clear() {
-
- foreach ( $_SESSION as $var => $val ) {
- unset( $_SESSION[$var] );
- }
-
- } // clear
-
- /**
- * Destroys the session including associated data.
- *
- * @return void
- */
- public function destroy() {
-
- // first unset the current session variables
- $this->remove_token();
- $this->clear();
- session_unset();
-
- // delete the session cookie if it exists - sets the expires time to be a year ago
- if( isset($_COOKIE[session_name()]) )
- setcookie( $this->session_name, '', time() - (60 * 60 * 24 * 365), '/' );
-
- // remove all traces of the session from the server
- session_destroy();
-
- // update session parameters
- $this->session_name = session_name();
- $this->session_id = session_id();
-
- } // destroy
-
- /**
- * Restarts the session.
- * Session data is cleared and a new session id is generated.
- *
- * @return void
- */
- public function restart() {
-
- // first unset the current session variables
- $this->clear();
- session_unset();
-
- // now generate a new session id
- session_regenerate_id();
-
- // update session parameters
- $this->session_name = session_name();
- $this->session_id = session_id();
-
- } // destroy
-
- /**
- * Sets a secure token against the session.
- * This is an additional security measure in addition to the session id and the USER_AGENT header check.
- * It means that even if an attacker managed to guess the session id of an authenticated user
- * AND managed to send the correct USER_AGENT header, they'd also have to send the correct token as well.<br /><br />
- * The secure token is only used for authenticated session (ie the user has already logged in),
- * which means attackers won't even know they need to send it unless they've successfully authenticated
- * a user, or they've examined the cookies stored on a client machine that has an authenticated session.
- *
- * @return boolean
- */
- public function set_token() {
-
- $fingerprint = SPF::hash();
- $_SESSION['session_token'] = $fingerprint;
- setcookie( 'session_token', $fingerprint );
-
- } // set_fingerprint
-
- /**
- * Removes the secure session token.
- *
- * @return void
- */
- public function remove_token() {
-
- unset($_SESSION['session_token']);
- setcookie( 'session_token', '', time() - 42000, '/' );
-
- } // remove_token
-
- /**
- * Determines whether the session has a secure token set.
- *
- * @return boolean
- */
- public function has_token() {
-
- return isset($_SESSION['session_token']);
-
- } // has_token
-
- /**
- * Verfies that the secure token held by the client matches the one stored in the session.
- * If the token has not been set this function will return true.
- *
- * @return boolean
- */
- public function check_token() {
-
- if( $this->has_token() )
- return isset($_COOKIE['session_token']) && $_COOKIE['session_token'] = $_SESSION['session_token'];
- else
- return true;
-
- } // check_token
-
- } // SPF_Session
-
- /**
- * Thrown when a session is invalid.
- *
- * @package spf
- * @author Simon Downes <simon@simondownes.co.uk>
- * @copyright Copyright (c) 2007, Simon Downes
- * @license http://www.opensource.org/licenses/mit-license.php
- */
- class SPF_Session_Exception extends SPF_Exception {
-
- public function __construct( $message ) {
- parent::__construct( $message );
- }
-
- } // SPF_Session_Exception
-
- ?>