PageRenderTime 43ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/libraries/joomla/session/session.php

https://bitbucket.org/izubizarreta/https-bitbucket.org-bityvip
PHP | 920 lines | 734 code | 32 blank | 154 comment | 8 complexity | 96ee642f64967127a349b680086ea669 MD5 | raw file
Possible License(s): LGPL-3.0, LGPL-2.0, JSON, GPL-2.0, BSD-3-Clause, LGPL-2.1, MIT
  1. <?php
  2. /**
  3. * @package Joomla.Platform
  4. * @subpackage Session
  5. *
  6. * @copyright Copyright (C) 2005 - 2012 Open Source Matters, Inc. All rights reserved.
  7. * @license GNU General Public License version 2 or later; see LICENSE
  8. */
  9. defined('JPATH_PLATFORM') or die;
  10. jimport('joomla.environment.request');
  11. /**
  12. * Class for managing HTTP sessions
  13. *
  14. * Provides access to session-state values as well as session-level
  15. * settings and lifetime management methods.
  16. * Based on the standard PHP session handling mechanism it provides
  17. * more advanced features such as expire timeouts.
  18. *
  19. * @package Joomla.Platform
  20. * @subpackage Session
  21. * @since 11.1
  22. */
  23. class JSession extends JObject
  24. {
  25. /**
  26. * Internal state.
  27. * One of 'active'|'expired'|'destroyed'|'error'
  28. *
  29. * @var string
  30. * @see getState()
  31. * @since 11.1
  32. */
  33. protected $_state = 'active';
  34. /**
  35. * Maximum age of unused session in minutes
  36. *
  37. * @var string
  38. * @since 11.1
  39. */
  40. protected $_expire = 15;
  41. /**
  42. * The session store object.
  43. *
  44. * @var JSessionStorage
  45. * @since 11.1
  46. */
  47. protected $_store = null;
  48. /**
  49. * Security policy.
  50. * List of checks that will be done.
  51. *
  52. * Default values:
  53. * - fix_browser
  54. * - fix_adress
  55. *
  56. * @var array
  57. * @since 11.1
  58. */
  59. protected $_security = array('fix_browser');
  60. /**
  61. * Force cookies to be SSL only
  62. * Default false
  63. *
  64. * @var boolean
  65. * @since 11.1
  66. */
  67. protected $_force_ssl = false;
  68. /**
  69. * @var JSession JSession instances container.
  70. * @since 11.3
  71. */
  72. protected static $instance;
  73. /**
  74. * Constructor
  75. *
  76. * @param string $store The type of storage for the session.
  77. * @param array $options Optional parameters
  78. *
  79. * @since 11.1
  80. */
  81. public function __construct($store = 'none', $options = array())
  82. {
  83. // Need to destroy any existing sessions started with session.auto_start
  84. if (session_id())
  85. {
  86. session_unset();
  87. session_destroy();
  88. }
  89. // Set default sessios save handler
  90. ini_set('session.save_handler', 'files');
  91. // Disable transparent sid support
  92. ini_set('session.use_trans_sid', '0');
  93. // Create handler
  94. $this->_store = JSessionStorage::getInstance($store, $options);
  95. // Set options
  96. $this->_setOptions($options);
  97. $this->_setCookieParams();
  98. // Load the session
  99. $this->_start();
  100. // Initialise the session
  101. $this->_setCounter();
  102. $this->_setTimers();
  103. $this->_state = 'active';
  104. // Perform security checks
  105. $this->_validate();
  106. }
  107. /**
  108. * Session object destructor
  109. *
  110. * @since 11.1
  111. */
  112. public function __destruct()
  113. {
  114. $this->close();
  115. }
  116. /**
  117. * Returns the global Session object, only creating it
  118. * if it doesn't already exist.
  119. *
  120. * @param string $handler The type of session handler.
  121. * @param array $options An array of configuration options.
  122. *
  123. * @return JSession The Session object.
  124. *
  125. * @since 11.1
  126. */
  127. public static function getInstance($handler, $options)
  128. {
  129. if (!is_object(self::$instance))
  130. {
  131. self::$instance = new JSession($handler, $options);
  132. }
  133. return self::$instance;
  134. }
  135. /**
  136. * Get current state of session
  137. *
  138. * @return string The session state
  139. *
  140. * @since 11.1
  141. */
  142. public function getState()
  143. {
  144. return $this->_state;
  145. }
  146. /**
  147. * Get expiration time in minutes
  148. *
  149. * @return integer The session expiration time in minutes
  150. *
  151. * @since 11.1
  152. */
  153. public function getExpire()
  154. {
  155. return $this->_expire;
  156. }
  157. /**
  158. * Get a session token, if a token isn't set yet one will be generated.
  159. *
  160. * Tokens are used to secure forms from spamming attacks. Once a token
  161. * has been generated the system will check the post request to see if
  162. * it is present, if not it will invalidate the session.
  163. *
  164. * @param boolean $forceNew If true, force a new token to be created
  165. *
  166. * @return string The session token
  167. *
  168. * @since 11.1
  169. */
  170. public function getToken($forceNew = false)
  171. {
  172. $token = $this->get('session.token');
  173. // Create a token
  174. if ($token === null || $forceNew)
  175. {
  176. $token = $this->_createToken(12);
  177. $this->set('session.token', $token);
  178. }
  179. return $token;
  180. }
  181. /**
  182. * Method to determine if a token exists in the session. If not the
  183. * session will be set to expired
  184. *
  185. * @param string $tCheck Hashed token to be verified
  186. * @param boolean $forceExpire If true, expires the session
  187. *
  188. * @return boolean
  189. *
  190. * @since 11.1
  191. */
  192. public function hasToken($tCheck, $forceExpire = true)
  193. {
  194. // Check if a token exists in the session
  195. $tStored = $this->get('session.token');
  196. // Check token
  197. if (($tStored !== $tCheck))
  198. {
  199. if ($forceExpire)
  200. {
  201. $this->_state = 'expired';
  202. }
  203. return false;
  204. }
  205. return true;
  206. }
  207. /**
  208. * Method to determine a hash for anti-spoofing variable names
  209. *
  210. * @param boolean $forceNew If true, force a new token to be created
  211. *
  212. * @return string Hashed var name
  213. *
  214. * @since 11.1
  215. */
  216. public static function getFormToken($forceNew = false)
  217. {
  218. $user = JFactory::getUser();
  219. $session = JFactory::getSession();
  220. $hash = JApplication::getHash($user->get('id', 0) . $session->getToken($forceNew));
  221. return $hash;
  222. }
  223. /**
  224. * Checks for a form token in the request.
  225. *
  226. * Use in conjunction with JHtml::_('form.token') or JSession::getFormToken.
  227. *
  228. * @param string $method The request method in which to look for the token key.
  229. *
  230. * @return boolean True if found and valid, false otherwise.
  231. *
  232. * @since 12.1
  233. */
  234. public static function checkToken($method = 'post')
  235. {
  236. if ($method == 'default')
  237. {
  238. trigger_error("JSession::checkToken() doesn't support 'default' for the method parameter.", E_USER_ERROR);
  239. return false;
  240. }
  241. $token = self::getFormToken();
  242. $app = JFactory::getApplication();
  243. if (!JRequest::getVar($token, '', $method, 'alnum'))
  244. {
  245. $session = JFactory::getSession();
  246. if ($session->isNew())
  247. {
  248. // Redirect to login screen.
  249. $app->redirect(JRoute::_('index.php'), JText::_('JLIB_ENVIRONMENT_SESSION_EXPIRED'));
  250. $app->close();
  251. }
  252. else
  253. {
  254. return false;
  255. }
  256. }
  257. else
  258. {
  259. return true;
  260. }
  261. }
  262. /**
  263. * Get session name
  264. *
  265. * @return string The session name
  266. *
  267. * @since 11.1
  268. */
  269. public function getName()
  270. {
  271. if ($this->_state === 'destroyed')
  272. {
  273. // @TODO : raise error
  274. return null;
  275. }
  276. return session_name();
  277. }
  278. /**
  279. * Get session id
  280. *
  281. * @return string The session name
  282. *
  283. * @since 11.1
  284. */
  285. public function getId()
  286. {
  287. if ($this->_state === 'destroyed')
  288. {
  289. // @TODO : raise error
  290. return null;
  291. }
  292. return session_id();
  293. }
  294. /**
  295. * Get the session handlers
  296. *
  297. * @return array An array of available session handlers
  298. *
  299. * @since 11.1
  300. */
  301. public static function getStores()
  302. {
  303. jimport('joomla.filesystem.folder');
  304. $handlers = JFolder::files(dirname(__FILE__) . '/storage', '.php$');
  305. $names = array();
  306. foreach ($handlers as $handler)
  307. {
  308. $name = substr($handler, 0, strrpos($handler, '.'));
  309. $class = 'JSessionStorage' . ucfirst($name);
  310. // Load the class only if needed
  311. if (!class_exists($class))
  312. {
  313. require_once dirname(__FILE__) . '/storage/' . $name . '.php';
  314. }
  315. if (call_user_func_array(array(trim($class), 'test'), array()))
  316. {
  317. $names[] = $name;
  318. }
  319. }
  320. return $names;
  321. }
  322. /**
  323. * Check whether this session is currently created
  324. *
  325. * @return boolean True on success.
  326. *
  327. * @since 11.1
  328. */
  329. public function isNew()
  330. {
  331. $counter = $this->get('session.counter');
  332. if ($counter === 1)
  333. {
  334. return true;
  335. }
  336. return false;
  337. }
  338. /**
  339. * Get data from the session store
  340. *
  341. * @param string $name Name of a variable
  342. * @param mixed $default Default value of a variable if not set
  343. * @param string $namespace Namespace to use, default to 'default'
  344. *
  345. * @return mixed Value of a variable
  346. *
  347. * @since 11.1
  348. */
  349. public function get($name, $default = null, $namespace = 'default')
  350. {
  351. // Add prefix to namespace to avoid collisions
  352. $namespace = '__' . $namespace;
  353. if ($this->_state !== 'active' && $this->_state !== 'expired')
  354. {
  355. // @TODO :: generated error here
  356. $error = null;
  357. return $error;
  358. }
  359. if (isset($_SESSION[$namespace][$name]))
  360. {
  361. return $_SESSION[$namespace][$name];
  362. }
  363. return $default;
  364. }
  365. /**
  366. * Set data into the session store.
  367. *
  368. * @param string $name Name of a variable.
  369. * @param mixed $value Value of a variable.
  370. * @param string $namespace Namespace to use, default to 'default'.
  371. *
  372. * @return mixed Old value of a variable.
  373. *
  374. * @since 11.1
  375. */
  376. public function set($name, $value = null, $namespace = 'default')
  377. {
  378. // Add prefix to namespace to avoid collisions
  379. $namespace = '__' . $namespace;
  380. if ($this->_state !== 'active')
  381. {
  382. // @TODO :: generated error here
  383. return null;
  384. }
  385. $old = isset($_SESSION[$namespace][$name]) ? $_SESSION[$namespace][$name] : null;
  386. if (null === $value)
  387. {
  388. unset($_SESSION[$namespace][$name]);
  389. }
  390. else
  391. {
  392. $_SESSION[$namespace][$name] = $value;
  393. }
  394. return $old;
  395. }
  396. /**
  397. * Check whether data exists in the session store
  398. *
  399. * @param string $name Name of variable
  400. * @param string $namespace Namespace to use, default to 'default'
  401. *
  402. * @return boolean True if the variable exists
  403. *
  404. * @since 11.1
  405. */
  406. public function has($name, $namespace = 'default')
  407. {
  408. // Add prefix to namespace to avoid collisions.
  409. $namespace = '__' . $namespace;
  410. if ($this->_state !== 'active')
  411. {
  412. // @TODO :: generated error here
  413. return null;
  414. }
  415. return isset($_SESSION[$namespace][$name]);
  416. }
  417. /**
  418. * Unset data from the session store
  419. *
  420. * @param string $name Name of variable
  421. * @param string $namespace Namespace to use, default to 'default'
  422. *
  423. * @return mixed The value from session or NULL if not set
  424. *
  425. * @since 11.1
  426. */
  427. public function clear($name, $namespace = 'default')
  428. {
  429. // Add prefix to namespace to avoid collisions
  430. $namespace = '__' . $namespace;
  431. if ($this->_state !== 'active')
  432. {
  433. // @TODO :: generated error here
  434. return null;
  435. }
  436. $value = null;
  437. if (isset($_SESSION[$namespace][$name]))
  438. {
  439. $value = $_SESSION[$namespace][$name];
  440. unset($_SESSION[$namespace][$name]);
  441. }
  442. return $value;
  443. }
  444. /**
  445. * Start a session.
  446. *
  447. * Creates a session (or resumes the current one based on the state of the session)
  448. *
  449. * @return boolean true on success
  450. *
  451. * @since 11.1
  452. */
  453. protected function _start()
  454. {
  455. // Start session if not started
  456. if ($this->_state == 'restart')
  457. {
  458. session_id($this->_createId());
  459. }
  460. else
  461. {
  462. $session_name = session_name();
  463. if (!JRequest::getVar($session_name, false, 'COOKIE'))
  464. {
  465. if (JRequest::getVar($session_name))
  466. {
  467. session_id(JRequest::getVar($session_name));
  468. setcookie($session_name, '', time() - 3600);
  469. }
  470. }
  471. }
  472. session_cache_limiter('none');
  473. session_start();
  474. $_SESSION["plan"]=0;
  475. return true;
  476. }
  477. /**
  478. * Frees all session variables and destroys all data registered to a session
  479. *
  480. * This method resets the $_SESSION variable and destroys all of the data associated
  481. * with the current session in its storage (file or DB). It forces new session to be
  482. * started after this method is called. It does not unset the session cookie.
  483. *
  484. * @return boolean True on success
  485. *
  486. * @see session_destroy()
  487. * @see session_unset()
  488. * @since 11.1
  489. */
  490. public function destroy()
  491. {
  492. // Session was already destroyed
  493. if ($this->_state === 'destroyed')
  494. {
  495. return true;
  496. }
  497. /*
  498. * In order to kill the session altogether, such as to log the user out, the session id
  499. * must also be unset. If a cookie is used to propagate the session id (default behavior),
  500. * then the session cookie must be deleted.
  501. */
  502. if (isset($_COOKIE[session_name()]))
  503. {
  504. $config = JFactory::getConfig();
  505. $cookie_domain = $config->get('cookie_domain', '');
  506. $cookie_path = $config->get('cookie_path', '/');
  507. setcookie(session_name(), '', time() - 42000, $cookie_path, $cookie_domain);
  508. }
  509. session_unset();
  510. session_destroy();
  511. $this->_state = 'destroyed';
  512. return true;
  513. }
  514. /**
  515. * Restart an expired or locked session.
  516. *
  517. * @return boolean True on success
  518. *
  519. * @see destroy
  520. * @since 11.1
  521. */
  522. public function restart()
  523. {
  524. $this->destroy();
  525. if ($this->_state !== 'destroyed')
  526. {
  527. // @TODO :: generated error here
  528. return false;
  529. }
  530. // Re-register the session handler after a session has been destroyed, to avoid PHP bug
  531. $this->_store->register();
  532. $this->_state = 'restart';
  533. // Regenerate session id
  534. $id = $this->_createId();
  535. session_id($id);
  536. $this->_start();
  537. $this->_state = 'active';
  538. $this->_validate();
  539. $this->_setCounter();
  540. return true;
  541. }
  542. /**
  543. * Create a new session and copy variables from the old one
  544. *
  545. * @return boolean $result true on success
  546. *
  547. * @since 11.1
  548. */
  549. public function fork()
  550. {
  551. if ($this->_state !== 'active')
  552. {
  553. // @TODO :: generated error here
  554. return false;
  555. }
  556. // Save values
  557. $values = $_SESSION;
  558. // Keep session config
  559. $trans = ini_get('session.use_trans_sid');
  560. if ($trans)
  561. {
  562. ini_set('session.use_trans_sid', 0);
  563. }
  564. $cookie = session_get_cookie_params();
  565. // Create new session id
  566. $id = $this->_createId();
  567. // Kill session
  568. session_destroy();
  569. // Re-register the session store after a session has been destroyed, to avoid PHP bug
  570. $this->_store->register();
  571. // Restore config
  572. ini_set('session.use_trans_sid', $trans);
  573. session_set_cookie_params($cookie['lifetime'], $cookie['path'], $cookie['domain'], $cookie['secure']);
  574. // Restart session with new id
  575. session_id($id);
  576. session_start();
  577. return true;
  578. }
  579. /**
  580. * Writes session data and ends session
  581. *
  582. * Session data is usually stored after your script terminated without the need
  583. * to call JSession::close(), but as session data is locked to prevent concurrent
  584. * writes only one script may operate on a session at any time. When using
  585. * framesets together with sessions you will experience the frames loading one
  586. * by one due to this locking. You can reduce the time needed to load all the
  587. * frames by ending the session as soon as all changes to session variables are
  588. * done.
  589. *
  590. * @return void
  591. *
  592. * @see session_write_close()
  593. * @since 11.1
  594. */
  595. public function close()
  596. {
  597. session_write_close();
  598. }
  599. /**
  600. * Create a session id
  601. *
  602. * @return string Session ID
  603. *
  604. * @since 11.1
  605. */
  606. protected function _createId()
  607. {
  608. $id = 0;
  609. while (strlen($id) < 32)
  610. {
  611. $id .= mt_rand(0, mt_getrandmax());
  612. }
  613. $id = md5(uniqid($id, true));
  614. return $id;
  615. }
  616. /**
  617. * Set session cookie parameters
  618. *
  619. * @return void
  620. *
  621. * @since 11.1
  622. */
  623. protected function _setCookieParams()
  624. {
  625. $cookie = session_get_cookie_params();
  626. if ($this->_force_ssl)
  627. {
  628. $cookie['secure'] = true;
  629. }
  630. $config = JFactory::getConfig();
  631. if ($config->get('cookie_domain', '') != '')
  632. {
  633. $cookie['domain'] = $config->get('cookie_domain');
  634. }
  635. if ($config->get('cookie_path', '') != '')
  636. {
  637. $cookie['path'] = $config->get('cookie_path');
  638. }
  639. session_set_cookie_params($cookie['lifetime'], $cookie['path'], $cookie['domain'], $cookie['secure']);
  640. }
  641. /**
  642. * Create a token-string
  643. *
  644. * @param integer $length Length of string
  645. *
  646. * @return string Generated token
  647. *
  648. * @since 11.1
  649. */
  650. protected function _createToken($length = 32)
  651. {
  652. static $chars = '0123456789abcdef';
  653. $max = strlen($chars) - 1;
  654. $token = '';
  655. $name = session_name();
  656. for ($i = 0; $i < $length; ++$i)
  657. {
  658. $token .= $chars[(rand(0, $max))];
  659. }
  660. return md5($token . $name);
  661. }
  662. /**
  663. * Set counter of session usage
  664. *
  665. * @return boolean True on success
  666. *
  667. * @since 11.1
  668. */
  669. protected function _setCounter()
  670. {
  671. $counter = $this->get('session.counter', 0);
  672. ++$counter;
  673. $this->set('session.counter', $counter);
  674. return true;
  675. }
  676. /**
  677. * Set the session timers
  678. *
  679. * @return boolean True on success
  680. *
  681. * @since 11.1
  682. */
  683. protected function _setTimers()
  684. {
  685. if (!$this->has('session.timer.start'))
  686. {
  687. $start = time();
  688. $this->set('session.timer.start', $start);
  689. $this->set('session.timer.last', $start);
  690. $this->set('session.timer.now', $start);
  691. }
  692. $this->set('session.timer.last', $this->get('session.timer.now'));
  693. $this->set('session.timer.now', time());
  694. return true;
  695. }
  696. /**
  697. * Set additional session options
  698. *
  699. * @param array &$options List of parameter
  700. *
  701. * @return boolean True on success
  702. *
  703. * @since 11.1
  704. */
  705. protected function _setOptions(&$options)
  706. {
  707. // Set name
  708. if (isset($options['name']))
  709. {
  710. session_name(md5($options['name']));
  711. }
  712. // Set id
  713. if (isset($options['id']))
  714. {
  715. session_id($options['id']);
  716. }
  717. // Set expire time
  718. if (isset($options['expire']))
  719. {
  720. $this->_expire = $options['expire'];
  721. }
  722. // Get security options
  723. if (isset($options['security']))
  724. {
  725. $this->_security = explode(',', $options['security']);
  726. }
  727. if (isset($options['force_ssl']))
  728. {
  729. $this->_force_ssl = (bool) $options['force_ssl'];
  730. }
  731. // Sync the session maxlifetime
  732. ini_set('session.gc_maxlifetime', $this->_expire);
  733. return true;
  734. }
  735. /**
  736. * Do some checks for security reason
  737. *
  738. * - timeout check (expire)
  739. * - ip-fixiation
  740. * - browser-fixiation
  741. *
  742. * If one check failed, session data has to be cleaned.
  743. *
  744. * @param boolean $restart Reactivate session
  745. *
  746. * @return boolean True on success
  747. *
  748. * @see http://shiflett.org/articles/the-truth-about-sessions
  749. * @since 11.1
  750. */
  751. protected function _validate($restart = false)
  752. {
  753. // Allow to restart a session
  754. if ($restart)
  755. {
  756. $this->_state = 'active';
  757. $this->set('session.client.address', null);
  758. $this->set('session.client.forwarded', null);
  759. $this->set('session.client.browser', null);
  760. $this->set('session.token', null);
  761. }
  762. // Check if session has expired
  763. if ($this->_expire)
  764. {
  765. $curTime = $this->get('session.timer.now', 0);
  766. $maxTime = $this->get('session.timer.last', 0) + $this->_expire;
  767. // Empty session variables
  768. if ($maxTime < $curTime)
  769. {
  770. $this->_state = 'expired';
  771. return false;
  772. }
  773. }
  774. // Record proxy forwarded for in the session in case we need it later
  775. if (isset($_SERVER['HTTP_X_FORWARDED_FOR']))
  776. {
  777. $this->set('session.client.forwarded', $_SERVER['HTTP_X_FORWARDED_FOR']);
  778. }
  779. // Check for client address
  780. if (in_array('fix_adress', $this->_security) && isset($_SERVER['REMOTE_ADDR']))
  781. {
  782. $ip = $this->get('session.client.address');
  783. if ($ip === null)
  784. {
  785. $this->set('session.client.address', $_SERVER['REMOTE_ADDR']);
  786. }
  787. elseif ($_SERVER['REMOTE_ADDR'] !== $ip)
  788. {
  789. $this->_state = 'error';
  790. return false;
  791. }
  792. }
  793. // Check for clients browser
  794. if (in_array('fix_browser', $this->_security) && isset($_SERVER['HTTP_USER_AGENT']))
  795. {
  796. $browser = $this->get('session.client.browser');
  797. if ($browser === null)
  798. {
  799. $this->set('session.client.browser', $_SERVER['HTTP_USER_AGENT']);
  800. }
  801. elseif ($_SERVER['HTTP_USER_AGENT'] !== $browser)
  802. {
  803. // @todo remove code: $this->_state = 'error';
  804. // @todo remove code: return false;
  805. }
  806. }
  807. return true;
  808. }
  809. }