1010 lines
21 KiB
PHP
1010 lines
21 KiB
PHP
|
<?php
|
||
|
/**
|
||
|
* @package Joomla.Platform
|
||
|
* @subpackage Session
|
||
|
*
|
||
|
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
|
||
|
* @license GNU General Public License version 2 or later; see LICENSE
|
||
|
*/
|
||
|
|
||
|
defined('JPATH_PLATFORM') or die;
|
||
|
|
||
|
/**
|
||
|
* Class for managing HTTP sessions
|
||
|
*
|
||
|
* Provides access to session-state values as well as session-level
|
||
|
* settings and lifetime management methods.
|
||
|
* Based on the standard PHP session handling mechanism it provides
|
||
|
* more advanced features such as expire timeouts.
|
||
|
*
|
||
|
* @package Joomla.Platform
|
||
|
* @subpackage Session
|
||
|
* @since 11.1
|
||
|
*/
|
||
|
class JSession implements IteratorAggregate
|
||
|
{
|
||
|
/**
|
||
|
* Internal state.
|
||
|
* One of 'inactive'|'active'|'expired'|'destroyed'|'error'
|
||
|
*
|
||
|
* @var string
|
||
|
* @see getState()
|
||
|
* @since 11.1
|
||
|
*/
|
||
|
protected $_state = 'inactive';
|
||
|
|
||
|
/**
|
||
|
* Maximum age of unused session in minutes
|
||
|
*
|
||
|
* @var string
|
||
|
* @since 11.1
|
||
|
*/
|
||
|
protected $_expire = 15;
|
||
|
|
||
|
/**
|
||
|
* The session store object.
|
||
|
*
|
||
|
* @var JSessionStorage
|
||
|
* @since 11.1
|
||
|
*/
|
||
|
protected $_store = null;
|
||
|
|
||
|
/**
|
||
|
* Security policy.
|
||
|
* List of checks that will be done.
|
||
|
*
|
||
|
* Default values:
|
||
|
* - fix_browser
|
||
|
* - fix_adress
|
||
|
*
|
||
|
* @var array
|
||
|
* @since 11.1
|
||
|
*/
|
||
|
protected $_security = array('fix_browser');
|
||
|
|
||
|
/**
|
||
|
* Force cookies to be SSL only
|
||
|
* Default false
|
||
|
*
|
||
|
* @var boolean
|
||
|
* @since 11.1
|
||
|
*/
|
||
|
protected $_force_ssl = false;
|
||
|
|
||
|
/**
|
||
|
* @var JSession JSession instances container.
|
||
|
* @since 11.3
|
||
|
*/
|
||
|
protected static $instance;
|
||
|
|
||
|
/**
|
||
|
* @var string
|
||
|
* @since 12.2
|
||
|
*/
|
||
|
protected $storeName;
|
||
|
|
||
|
/**
|
||
|
* Holds the JInput object
|
||
|
*
|
||
|
* @var JInput
|
||
|
* @since 12.2
|
||
|
*/
|
||
|
private $_input = null;
|
||
|
|
||
|
/**
|
||
|
* Holds the event dispatcher object
|
||
|
*
|
||
|
* @var JEventDispatcher
|
||
|
* @since 12.2
|
||
|
*/
|
||
|
private $_dispatcher = null;
|
||
|
|
||
|
/**
|
||
|
* Constructor
|
||
|
*
|
||
|
* @param string $store The type of storage for the session.
|
||
|
* @param array $options Optional parameters
|
||
|
*
|
||
|
* @since 11.1
|
||
|
*/
|
||
|
public function __construct($store = 'none', array $options = array())
|
||
|
{
|
||
|
// Need to destroy any existing sessions started with session.auto_start
|
||
|
if (session_id())
|
||
|
{
|
||
|
session_unset();
|
||
|
session_destroy();
|
||
|
}
|
||
|
|
||
|
// Disable transparent sid support
|
||
|
ini_set('session.use_trans_sid', '0');
|
||
|
|
||
|
// Only allow the session ID to come from cookies and nothing else.
|
||
|
ini_set('session.use_only_cookies', '1');
|
||
|
|
||
|
// Create handler
|
||
|
$this->_store = JSessionStorage::getInstance($store, $options);
|
||
|
|
||
|
$this->storeName = $store;
|
||
|
|
||
|
// Set options
|
||
|
$this->_setOptions($options);
|
||
|
|
||
|
$this->_setCookieParams();
|
||
|
|
||
|
$this->_state = 'inactive';
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Magic method to get read-only access to properties.
|
||
|
*
|
||
|
* @param string $name Name of property to retrieve
|
||
|
*
|
||
|
* @return mixed The value of the property
|
||
|
*
|
||
|
* @since 12.2
|
||
|
*/
|
||
|
public function __get($name)
|
||
|
{
|
||
|
if ($name === 'storeName')
|
||
|
{
|
||
|
return $this->$name;
|
||
|
}
|
||
|
|
||
|
if ($name === 'state' || $name === 'expire')
|
||
|
{
|
||
|
$property = '_' . $name;
|
||
|
return $this->$property;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the global Session object, only creating it
|
||
|
* if it doesn't already exist.
|
||
|
*
|
||
|
* @param string $handler The type of session handler.
|
||
|
* @param array $options An array of configuration options.
|
||
|
*
|
||
|
* @return JSession The Session object.
|
||
|
*
|
||
|
* @since 11.1
|
||
|
*/
|
||
|
public static function getInstance($handler, $options)
|
||
|
{
|
||
|
if (!is_object(self::$instance))
|
||
|
{
|
||
|
self::$instance = new JSession($handler, $options);
|
||
|
}
|
||
|
|
||
|
return self::$instance;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get current state of session
|
||
|
*
|
||
|
* @return string The session state
|
||
|
*
|
||
|
* @since 11.1
|
||
|
*/
|
||
|
public function getState()
|
||
|
{
|
||
|
return $this->_state;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get expiration time in minutes
|
||
|
*
|
||
|
* @return integer The session expiration time in minutes
|
||
|
*
|
||
|
* @since 11.1
|
||
|
*/
|
||
|
public function getExpire()
|
||
|
{
|
||
|
return $this->_expire;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get a session token, if a token isn't set yet one will be generated.
|
||
|
*
|
||
|
* Tokens are used to secure forms from spamming attacks. Once a token
|
||
|
* has been generated the system will check the post request to see if
|
||
|
* it is present, if not it will invalidate the session.
|
||
|
*
|
||
|
* @param boolean $forceNew If true, force a new token to be created
|
||
|
*
|
||
|
* @return string The session token
|
||
|
*
|
||
|
* @since 11.1
|
||
|
*/
|
||
|
public function getToken($forceNew = false)
|
||
|
{
|
||
|
$token = $this->get('session.token');
|
||
|
|
||
|
// Create a token
|
||
|
if ($token === null || $forceNew)
|
||
|
{
|
||
|
$token = $this->_createToken(12);
|
||
|
$this->set('session.token', $token);
|
||
|
}
|
||
|
|
||
|
return $token;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Method to determine if a token exists in the session. If not the
|
||
|
* session will be set to expired
|
||
|
*
|
||
|
* @param string $tCheck Hashed token to be verified
|
||
|
* @param boolean $forceExpire If true, expires the session
|
||
|
*
|
||
|
* @return boolean
|
||
|
*
|
||
|
* @since 11.1
|
||
|
*/
|
||
|
public function hasToken($tCheck, $forceExpire = true)
|
||
|
{
|
||
|
// Check if a token exists in the session
|
||
|
$tStored = $this->get('session.token');
|
||
|
|
||
|
// Check token
|
||
|
if (($tStored !== $tCheck))
|
||
|
{
|
||
|
if ($forceExpire)
|
||
|
{
|
||
|
$this->_state = 'expired';
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Method to determine a hash for anti-spoofing variable names
|
||
|
*
|
||
|
* @param boolean $forceNew If true, force a new token to be created
|
||
|
*
|
||
|
* @return string Hashed var name
|
||
|
*
|
||
|
* @since 11.1
|
||
|
*/
|
||
|
public static function getFormToken($forceNew = false)
|
||
|
{
|
||
|
$user = JFactory::getUser();
|
||
|
$session = JFactory::getSession();
|
||
|
|
||
|
// TODO: Decouple from legacy JApplication class.
|
||
|
if (is_callable(array('JApplication', 'getHash')))
|
||
|
{
|
||
|
$hash = JApplication::getHash($user->get('id', 0) . $session->getToken($forceNew));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
$hash = md5(JFactory::getApplication()->get('secret') . $user->get('id', 0) . $session->getToken($forceNew));
|
||
|
}
|
||
|
|
||
|
return $hash;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieve an external iterator.
|
||
|
*
|
||
|
* @return ArrayIterator Return an ArrayIterator of $_SESSION.
|
||
|
*
|
||
|
* @since 12.2
|
||
|
*/
|
||
|
public function getIterator()
|
||
|
{
|
||
|
return new ArrayIterator($_SESSION);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Checks for a form token in the request.
|
||
|
*
|
||
|
* Use in conjunction with JHtml::_('form.token') or JSession::getFormToken.
|
||
|
*
|
||
|
* @param string $method The request method in which to look for the token key.
|
||
|
*
|
||
|
* @return boolean True if found and valid, false otherwise.
|
||
|
*
|
||
|
* @since 12.1
|
||
|
*/
|
||
|
public static function checkToken($method = 'post')
|
||
|
{
|
||
|
$token = self::getFormToken();
|
||
|
$app = JFactory::getApplication();
|
||
|
|
||
|
if (!$app->input->$method->get($token, '', 'alnum'))
|
||
|
{
|
||
|
$session = JFactory::getSession();
|
||
|
if ($session->isNew())
|
||
|
{
|
||
|
// Redirect to login screen.
|
||
|
$app->redirect(JRoute::_('index.php'), JText::_('JLIB_ENVIRONMENT_SESSION_EXPIRED'));
|
||
|
$app->close();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get session name
|
||
|
*
|
||
|
* @return string The session name
|
||
|
*
|
||
|
* @since 11.1
|
||
|
*/
|
||
|
public function getName()
|
||
|
{
|
||
|
if ($this->_state === 'destroyed')
|
||
|
{
|
||
|
// @TODO : raise error
|
||
|
return null;
|
||
|
}
|
||
|
return session_name();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get session id
|
||
|
*
|
||
|
* @return string The session name
|
||
|
*
|
||
|
* @since 11.1
|
||
|
*/
|
||
|
public function getId()
|
||
|
{
|
||
|
if ($this->_state === 'destroyed')
|
||
|
{
|
||
|
// @TODO : raise error
|
||
|
return null;
|
||
|
}
|
||
|
return session_id();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the session handlers
|
||
|
*
|
||
|
* @return array An array of available session handlers
|
||
|
*
|
||
|
* @since 11.1
|
||
|
*/
|
||
|
public static function getStores()
|
||
|
{
|
||
|
$connectors = array();
|
||
|
|
||
|
// Get an iterator and loop trough the driver classes.
|
||
|
$iterator = new DirectoryIterator(__DIR__ . '/storage');
|
||
|
|
||
|
foreach ($iterator as $file)
|
||
|
{
|
||
|
$fileName = $file->getFilename();
|
||
|
|
||
|
// Only load for php files.
|
||
|
// Note: DirectoryIterator::getExtension only available PHP >= 5.3.6
|
||
|
if (!$file->isFile() || substr($fileName, strrpos($fileName, '.') + 1) != 'php')
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Derive the class name from the type.
|
||
|
$class = str_ireplace('.php', '', 'JSessionStorage' . ucfirst(trim($fileName)));
|
||
|
|
||
|
// If the class doesn't exist we have nothing left to do but look at the next type. We did our best.
|
||
|
if (!class_exists($class))
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Sweet! Our class exists, so now we just need to know if it passes its test method.
|
||
|
if ($class::isSupported())
|
||
|
{
|
||
|
// Connector names should not have file extensions.
|
||
|
$connectors[] = str_ireplace('.php', '', $fileName);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return $connectors;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Shorthand to check if the session is active
|
||
|
*
|
||
|
* @return boolean
|
||
|
*
|
||
|
* @since 12.2
|
||
|
*/
|
||
|
public function isActive()
|
||
|
{
|
||
|
return (bool) ($this->_state == 'active');
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Check whether this session is currently created
|
||
|
*
|
||
|
* @return boolean True on success.
|
||
|
*
|
||
|
* @since 11.1
|
||
|
*/
|
||
|
public function isNew()
|
||
|
{
|
||
|
$counter = $this->get('session.counter');
|
||
|
return (bool) ($counter === 1);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Check whether this session is currently created
|
||
|
*
|
||
|
* @param JInput $input JInput object for the session to use.
|
||
|
* @param JEventDispatcher $dispatcher Dispatcher object for the session to use.
|
||
|
*
|
||
|
* @return void.
|
||
|
*
|
||
|
* @since 12.2
|
||
|
*/
|
||
|
public function initialise(JInput $input, JEventDispatcher $dispatcher = null)
|
||
|
{
|
||
|
$this->_input = $input;
|
||
|
$this->_dispatcher = $dispatcher;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get data from the session store
|
||
|
*
|
||
|
* @param string $name Name of a variable
|
||
|
* @param mixed $default Default value of a variable if not set
|
||
|
* @param string $namespace Namespace to use, default to 'default'
|
||
|
*
|
||
|
* @return mixed Value of a variable
|
||
|
*
|
||
|
* @since 11.1
|
||
|
*/
|
||
|
public function get($name, $default = null, $namespace = 'default')
|
||
|
{
|
||
|
// Add prefix to namespace to avoid collisions
|
||
|
$namespace = '__' . $namespace;
|
||
|
|
||
|
if ($this->_state !== 'active' && $this->_state !== 'expired')
|
||
|
{
|
||
|
// @TODO :: generated error here
|
||
|
$error = null;
|
||
|
return $error;
|
||
|
}
|
||
|
|
||
|
if (isset($_SESSION[$namespace][$name]))
|
||
|
{
|
||
|
return $_SESSION[$namespace][$name];
|
||
|
}
|
||
|
return $default;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set data into the session store.
|
||
|
*
|
||
|
* @param string $name Name of a variable.
|
||
|
* @param mixed $value Value of a variable.
|
||
|
* @param string $namespace Namespace to use, default to 'default'.
|
||
|
*
|
||
|
* @return mixed Old value of a variable.
|
||
|
*
|
||
|
* @since 11.1
|
||
|
*/
|
||
|
public function set($name, $value = null, $namespace = 'default')
|
||
|
{
|
||
|
// Add prefix to namespace to avoid collisions
|
||
|
$namespace = '__' . $namespace;
|
||
|
|
||
|
if ($this->_state !== 'active')
|
||
|
{
|
||
|
// @TODO :: generated error here
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
$old = isset($_SESSION[$namespace][$name]) ? $_SESSION[$namespace][$name] : null;
|
||
|
|
||
|
if (null === $value)
|
||
|
{
|
||
|
unset($_SESSION[$namespace][$name]);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
$_SESSION[$namespace][$name] = $value;
|
||
|
}
|
||
|
|
||
|
return $old;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Check whether data exists in the session store
|
||
|
*
|
||
|
* @param string $name Name of variable
|
||
|
* @param string $namespace Namespace to use, default to 'default'
|
||
|
*
|
||
|
* @return boolean True if the variable exists
|
||
|
*
|
||
|
* @since 11.1
|
||
|
*/
|
||
|
public function has($name, $namespace = 'default')
|
||
|
{
|
||
|
// Add prefix to namespace to avoid collisions.
|
||
|
$namespace = '__' . $namespace;
|
||
|
|
||
|
if ($this->_state !== 'active')
|
||
|
{
|
||
|
// @TODO :: generated error here
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
return isset($_SESSION[$namespace][$name]);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Unset data from the session store
|
||
|
*
|
||
|
* @param string $name Name of variable
|
||
|
* @param string $namespace Namespace to use, default to 'default'
|
||
|
*
|
||
|
* @return mixed The value from session or NULL if not set
|
||
|
*
|
||
|
* @since 11.1
|
||
|
*/
|
||
|
public function clear($name, $namespace = 'default')
|
||
|
{
|
||
|
// Add prefix to namespace to avoid collisions
|
||
|
$namespace = '__' . $namespace;
|
||
|
|
||
|
if ($this->_state !== 'active')
|
||
|
{
|
||
|
// @TODO :: generated error here
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
$value = null;
|
||
|
if (isset($_SESSION[$namespace][$name]))
|
||
|
{
|
||
|
$value = $_SESSION[$namespace][$name];
|
||
|
unset($_SESSION[$namespace][$name]);
|
||
|
}
|
||
|
|
||
|
return $value;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Start a session.
|
||
|
*
|
||
|
* @return void
|
||
|
*
|
||
|
* @since 12.2
|
||
|
*/
|
||
|
public function start()
|
||
|
{
|
||
|
if ($this->_state === 'active')
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
$this->_start();
|
||
|
|
||
|
$this->_state = 'active';
|
||
|
|
||
|
// Initialise the session
|
||
|
$this->_setCounter();
|
||
|
$this->_setTimers();
|
||
|
|
||
|
// Perform security checks
|
||
|
$this->_validate();
|
||
|
|
||
|
if ($this->_dispatcher instanceof JEventDispatcher)
|
||
|
{
|
||
|
$this->_dispatcher->trigger('onAfterSessionStart');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Start a session.
|
||
|
*
|
||
|
* Creates a session (or resumes the current one based on the state of the session)
|
||
|
*
|
||
|
* @return boolean true on success
|
||
|
*
|
||
|
* @since 11.1
|
||
|
*/
|
||
|
protected function _start()
|
||
|
{
|
||
|
// Start session if not started
|
||
|
if ($this->_state === 'restart')
|
||
|
{
|
||
|
session_regenerate_id(true);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
$session_name = session_name();
|
||
|
|
||
|
// Get the JInputCookie object
|
||
|
$cookie = $this->_input->cookie;
|
||
|
|
||
|
if (is_null($cookie->get($session_name)))
|
||
|
{
|
||
|
$session_clean = $this->_input->get($session_name, false, 'string');
|
||
|
|
||
|
if ($session_clean)
|
||
|
{
|
||
|
session_id($session_clean);
|
||
|
$cookie->set($session_name, '', time() - 3600);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Write and Close handlers are called after destructing objects since PHP 5.0.5.
|
||
|
* Thus destructors can use sessions but session handler can't use objects.
|
||
|
* So we are moving session closure before destructing objects.
|
||
|
*
|
||
|
* Replace with session_register_shutdown() when dropping compatibility with PHP 5.3
|
||
|
*/
|
||
|
register_shutdown_function('session_write_close');
|
||
|
|
||
|
session_cache_limiter('none');
|
||
|
session_start();
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Frees all session variables and destroys all data registered to a session
|
||
|
*
|
||
|
* This method resets the $_SESSION variable and destroys all of the data associated
|
||
|
* with the current session in its storage (file or DB). It forces new session to be
|
||
|
* started after this method is called. It does not unset the session cookie.
|
||
|
*
|
||
|
* @return boolean True on success
|
||
|
*
|
||
|
* @see session_destroy()
|
||
|
* @see session_unset()
|
||
|
* @since 11.1
|
||
|
*/
|
||
|
public function destroy()
|
||
|
{
|
||
|
// Session was already destroyed
|
||
|
if ($this->_state === 'destroyed')
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* In order to kill the session altogether, such as to log the user out, the session id
|
||
|
* must also be unset. If a cookie is used to propagate the session id (default behavior),
|
||
|
* then the session cookie must be deleted.
|
||
|
*/
|
||
|
if (isset($_COOKIE[session_name()]))
|
||
|
{
|
||
|
$config = JFactory::getConfig();
|
||
|
$cookie_domain = $config->get('cookie_domain', '');
|
||
|
$cookie_path = $config->get('cookie_path', '/');
|
||
|
setcookie(session_name(), '', time() - 42000, $cookie_path, $cookie_domain);
|
||
|
}
|
||
|
|
||
|
session_unset();
|
||
|
session_destroy();
|
||
|
|
||
|
$this->_state = 'destroyed';
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Restart an expired or locked session.
|
||
|
*
|
||
|
* @return boolean True on success
|
||
|
*
|
||
|
* @see destroy
|
||
|
* @since 11.1
|
||
|
*/
|
||
|
public function restart()
|
||
|
{
|
||
|
$this->destroy();
|
||
|
if ($this->_state !== 'destroyed')
|
||
|
{
|
||
|
// @TODO :: generated error here
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Re-register the session handler after a session has been destroyed, to avoid PHP bug
|
||
|
$this->_store->register();
|
||
|
|
||
|
$this->_state = 'restart';
|
||
|
|
||
|
// Regenerate session id
|
||
|
session_regenerate_id(true);
|
||
|
$this->_start();
|
||
|
$this->_state = 'active';
|
||
|
|
||
|
$this->_validate();
|
||
|
$this->_setCounter();
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Create a new session and copy variables from the old one
|
||
|
*
|
||
|
* @return boolean $result true on success
|
||
|
*
|
||
|
* @since 11.1
|
||
|
*/
|
||
|
public function fork()
|
||
|
{
|
||
|
if ($this->_state !== 'active')
|
||
|
{
|
||
|
// @TODO :: generated error here
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Keep session config
|
||
|
$cookie = session_get_cookie_params();
|
||
|
|
||
|
// Kill session
|
||
|
session_destroy();
|
||
|
|
||
|
// Re-register the session store after a session has been destroyed, to avoid PHP bug
|
||
|
$this->_store->register();
|
||
|
|
||
|
// Restore config
|
||
|
session_set_cookie_params($cookie['lifetime'], $cookie['path'], $cookie['domain'], $cookie['secure'], true);
|
||
|
|
||
|
// Restart session with new id
|
||
|
session_regenerate_id(true);
|
||
|
session_start();
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Writes session data and ends session
|
||
|
*
|
||
|
* Session data is usually stored after your script terminated without the need
|
||
|
* to call JSession::close(), but as session data is locked to prevent concurrent
|
||
|
* writes only one script may operate on a session at any time. When using
|
||
|
* framesets together with sessions you will experience the frames loading one
|
||
|
* by one due to this locking. You can reduce the time needed to load all the
|
||
|
* frames by ending the session as soon as all changes to session variables are
|
||
|
* done.
|
||
|
*
|
||
|
* @return void
|
||
|
*
|
||
|
* @see session_write_close()
|
||
|
* @since 11.1
|
||
|
*/
|
||
|
public function close()
|
||
|
{
|
||
|
session_write_close();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set session cookie parameters
|
||
|
*
|
||
|
* @return void
|
||
|
*
|
||
|
* @since 11.1
|
||
|
*/
|
||
|
protected function _setCookieParams()
|
||
|
{
|
||
|
$cookie = session_get_cookie_params();
|
||
|
if ($this->_force_ssl)
|
||
|
{
|
||
|
$cookie['secure'] = true;
|
||
|
}
|
||
|
|
||
|
$config = JFactory::getConfig();
|
||
|
|
||
|
if ($config->get('cookie_domain', '') != '')
|
||
|
{
|
||
|
$cookie['domain'] = $config->get('cookie_domain');
|
||
|
}
|
||
|
|
||
|
if ($config->get('cookie_path', '') != '')
|
||
|
{
|
||
|
$cookie['path'] = $config->get('cookie_path');
|
||
|
}
|
||
|
session_set_cookie_params($cookie['lifetime'], $cookie['path'], $cookie['domain'], $cookie['secure'], true);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Create a token-string
|
||
|
*
|
||
|
* @param integer $length Length of string
|
||
|
*
|
||
|
* @return string Generated token
|
||
|
*
|
||
|
* @since 11.1
|
||
|
*/
|
||
|
protected function _createToken($length = 32)
|
||
|
{
|
||
|
static $chars = '0123456789abcdef';
|
||
|
$max = strlen($chars) - 1;
|
||
|
$token = '';
|
||
|
$name = session_name();
|
||
|
for ($i = 0; $i < $length; ++$i)
|
||
|
{
|
||
|
$token .= $chars[(rand(0, $max))];
|
||
|
}
|
||
|
|
||
|
return md5($token . $name);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set counter of session usage
|
||
|
*
|
||
|
* @return boolean True on success
|
||
|
*
|
||
|
* @since 11.1
|
||
|
*/
|
||
|
protected function _setCounter()
|
||
|
{
|
||
|
$counter = $this->get('session.counter', 0);
|
||
|
++$counter;
|
||
|
|
||
|
$this->set('session.counter', $counter);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set the session timers
|
||
|
*
|
||
|
* @return boolean True on success
|
||
|
*
|
||
|
* @since 11.1
|
||
|
*/
|
||
|
protected function _setTimers()
|
||
|
{
|
||
|
if (!$this->has('session.timer.start'))
|
||
|
{
|
||
|
$start = time();
|
||
|
|
||
|
$this->set('session.timer.start', $start);
|
||
|
$this->set('session.timer.last', $start);
|
||
|
$this->set('session.timer.now', $start);
|
||
|
}
|
||
|
|
||
|
$this->set('session.timer.last', $this->get('session.timer.now'));
|
||
|
$this->set('session.timer.now', time());
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set additional session options
|
||
|
*
|
||
|
* @param array $options List of parameter
|
||
|
*
|
||
|
* @return boolean True on success
|
||
|
*
|
||
|
* @since 11.1
|
||
|
*/
|
||
|
protected function _setOptions(array $options)
|
||
|
{
|
||
|
// Set name
|
||
|
if (isset($options['name']))
|
||
|
{
|
||
|
session_name(md5($options['name']));
|
||
|
}
|
||
|
|
||
|
// Set id
|
||
|
if (isset($options['id']))
|
||
|
{
|
||
|
session_id($options['id']);
|
||
|
}
|
||
|
|
||
|
// Set expire time
|
||
|
if (isset($options['expire']))
|
||
|
{
|
||
|
$this->_expire = $options['expire'];
|
||
|
}
|
||
|
|
||
|
// Get security options
|
||
|
if (isset($options['security']))
|
||
|
{
|
||
|
$this->_security = explode(',', $options['security']);
|
||
|
}
|
||
|
|
||
|
if (isset($options['force_ssl']))
|
||
|
{
|
||
|
$this->_force_ssl = (bool) $options['force_ssl'];
|
||
|
}
|
||
|
|
||
|
// Sync the session maxlifetime
|
||
|
ini_set('session.gc_maxlifetime', $this->_expire);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Do some checks for security reason
|
||
|
*
|
||
|
* - timeout check (expire)
|
||
|
* - ip-fixiation
|
||
|
* - browser-fixiation
|
||
|
*
|
||
|
* If one check failed, session data has to be cleaned.
|
||
|
*
|
||
|
* @param boolean $restart Reactivate session
|
||
|
*
|
||
|
* @return boolean True on success
|
||
|
*
|
||
|
* @see http://shiflett.org/articles/the-truth-about-sessions
|
||
|
* @since 11.1
|
||
|
*/
|
||
|
protected function _validate($restart = false)
|
||
|
{
|
||
|
// Allow to restart a session
|
||
|
if ($restart)
|
||
|
{
|
||
|
$this->_state = 'active';
|
||
|
|
||
|
$this->set('session.client.address', null);
|
||
|
$this->set('session.client.forwarded', null);
|
||
|
$this->set('session.client.browser', null);
|
||
|
$this->set('session.token', null);
|
||
|
}
|
||
|
|
||
|
// Check if session has expired
|
||
|
if ($this->_expire)
|
||
|
{
|
||
|
$curTime = $this->get('session.timer.now', 0);
|
||
|
$maxTime = $this->get('session.timer.last', 0) + $this->_expire;
|
||
|
|
||
|
// Empty session variables
|
||
|
if ($maxTime < $curTime)
|
||
|
{
|
||
|
$this->_state = 'expired';
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Record proxy forwarded for in the session in case we need it later
|
||
|
if (isset($_SERVER['HTTP_X_FORWARDED_FOR']))
|
||
|
{
|
||
|
$this->set('session.client.forwarded', $_SERVER['HTTP_X_FORWARDED_FOR']);
|
||
|
}
|
||
|
|
||
|
// Check for client address
|
||
|
if (in_array('fix_adress', $this->_security) && isset($_SERVER['REMOTE_ADDR']))
|
||
|
{
|
||
|
$ip = $this->get('session.client.address');
|
||
|
|
||
|
if ($ip === null)
|
||
|
{
|
||
|
$this->set('session.client.address', $_SERVER['REMOTE_ADDR']);
|
||
|
}
|
||
|
elseif ($_SERVER['REMOTE_ADDR'] !== $ip)
|
||
|
{
|
||
|
$this->_state = 'error';
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Check for clients browser
|
||
|
if (in_array('fix_browser', $this->_security) && isset($_SERVER['HTTP_USER_AGENT']))
|
||
|
{
|
||
|
$browser = $this->get('session.client.browser');
|
||
|
|
||
|
if ($browser === null)
|
||
|
{
|
||
|
$this->set('session.client.browser', $_SERVER['HTTP_USER_AGENT']);
|
||
|
}
|
||
|
elseif ($_SERVER['HTTP_USER_AGENT'] !== $browser)
|
||
|
{
|
||
|
// @todo remove code: $this->_state = 'error';
|
||
|
// @todo remove code: return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
}
|