PageRenderTime 33ms CodeModel.GetById 7ms RepoModel.GetById 0ms app.codeStats 0ms

/SPF_Session.php

http://spf-php.googlecode.com/
PHP | 336 lines | 106 code | 60 blank | 170 comment | 13 complexity | 77f1fb6240e5afc46e0bb739a69b8075 MD5 | raw file
  1. <?php
  2. /**
  3. * SPF_Session
  4. * @package spf
  5. * @author Simon Downes <simon@simondownes.co.uk>
  6. * @copyright Copyright (c) 2007, Simon Downes
  7. * @license http://www.opensource.org/licenses/mit-license.php
  8. */
  9. /**
  10. * Base session management class.
  11. * As a basic session hijacking prevention technique, the $_SESSION['HTTP_USER_AGENT']
  12. * variable must remain constant between requests using the same session id.
  13. *
  14. * TODO: split functionality out into native and database drivers.
  15. *
  16. * @final
  17. * @package spf
  18. * @author Simon Downes <simon@simondownes.co.uk>
  19. * @copyright Copyright (c) 2007, Simon Downes
  20. * @license http://www.opensource.org/licenses/mit-license.php
  21. */
  22. final class SPF_Session extends SPF_Object {
  23. /**
  24. * The session id assigned by PHP (usually a 32 character alpha-numeric string).
  25. * @var string
  26. */
  27. public $session_id;
  28. /**
  29. * The name of the session.
  30. * This is the name of the cookie on client machines used to hold the session id.
  31. * @var string
  32. */
  33. public $session_name;
  34. /**
  35. * Maximum number of seconds allowed between requests using the same session.
  36. * @var integer
  37. */
  38. public $lifetime;
  39. /**
  40. * Constructor.
  41. * Session is initialised automatically upon creation of the object.
  42. *
  43. * @param string $name name of the cookie used to store the session id on the client.
  44. * @param integer $lifetime maximum number of seconds allowed between requests using the same session.
  45. * @return void
  46. */
  47. public function __construct( $name = '', $lifetime = 0 ) {
  48. parent::__construct();
  49. // initialise the session
  50. $this->session_name = $name ? $name : 'SPF_SESSION';
  51. $this->lifetime = (int) $lifetime;
  52. $this->start();
  53. } // __construct
  54. /**
  55. * Destructor.
  56. *
  57. * @return void
  58. */
  59. public function __destruct() {
  60. // write and pending data to storage
  61. session_write_close();
  62. parent::__destruct();
  63. }
  64. /**
  65. * Disable PHP5's cloning method so people can't make copies of the session instance.
  66. *
  67. * @return void
  68. */
  69. public function __clone() {
  70. throw new SPF_Session_Exception('Object cloning is not allowed for '. __CLASS__);
  71. }
  72. /**
  73. * Stores a name/value pair in the session.
  74. *
  75. * Using PHP5's overloading for setting and getting variables we can
  76. * use $session->var = $val and have it stored in the $_SESSION
  77. * variable. To set an email address, for instance you would do the
  78. * following:
  79. *
  80. * <code>
  81. * $session->email = 'user@example.com';
  82. * </code>
  83. *
  84. * This doesn't actually store 'user@example.com' into $session->email,
  85. * rather it is stored in $_SESSION['email'].
  86. *
  87. * @param string $var the name of the variable to be stored.
  88. * @param mixed $val the value to be stored.
  89. * @return void
  90. * @link http://us3.php.net/manual/en/language.oop5.overloading.php
  91. */
  92. public function __set( $var, $val ) {
  93. $_SESSION[$var] = $val;
  94. }
  95. /**
  96. * Returns the requested session variable.
  97. *
  98. * @param string $var the name of the variable to be retrieved.
  99. * @return mixed the value of the request variable.
  100. */
  101. public function __get( $var ) {
  102. return isset($_SESSION[$var]) ? $_SESSION[$var] : NULL;
  103. }
  104. /**
  105. * Determines if a variable exists in the session.
  106. *
  107. * @param string $var the name of the variable to look for.
  108. * @return boolean
  109. */
  110. public function __isset( $var ) {
  111. return isset($_SESSION[$var]);
  112. }
  113. /**
  114. * Removes a variable from the session.
  115. *
  116. * @param string $var the name of the variable to remove.
  117. * @return void
  118. */
  119. public function __unset( $var ) {
  120. unset($_SESSION[$var]);
  121. }
  122. /**
  123. * Determines if there is an active session.
  124. *
  125. * @return boolean
  126. */
  127. public function is_active() {
  128. return ($this->session_id !== NULL && strlen($this->session_id) == 32 );
  129. } // is_active
  130. /**
  131. * Initialises the session.
  132. *
  133. * @return void
  134. */
  135. public function start() {
  136. // if there is already an active session then do nothing
  137. if( $this->is_active() )
  138. return;
  139. // set the session name and load the session data
  140. session_name( $this->session_name );
  141. session_start();
  142. // store session parameters
  143. $this->session_name = session_name();
  144. $this->session_id = session_id();
  145. // if we have previously stored the USER_AGENT header then check that the
  146. // current header matches the stored one, and if not then throw and exception
  147. // as someone is probably attempting to hijack the session...
  148. if( isset($_SESSION['HTTP_USER_AGENT']) && $_SESSION['HTTP_USER_AGENT'] != md5($_SERVER['HTTP_USER_AGENT']) ) {
  149. $this->destroy();
  150. throw new SPF_Session_Exception('Attempt to hijack a session!');
  151. }
  152. // if no USER_AGENT header is currently stored then store it
  153. else
  154. $_SESSION['HTTP_USER_AGENT'] = md5($_SERVER['HTTP_USER_AGENT']);
  155. // check that the secure session token is valid if it exists
  156. if( !$this->check_token() ) {
  157. $this->destroy();
  158. throw new SPF_Session_Exception('Invalid Token');
  159. }
  160. // if session lifetime is enabled...
  161. if( $this->lifetime > 0 ) {
  162. // if expiry is set and less than the current timestamp then session has expired...
  163. if( isset($_SESSION['session_expires']) && $_SESSION['session_expires'] < time() ) {
  164. $this->destroy();
  165. throw new SPF_Session_Exception('Expired');
  166. }
  167. // otherwise we just need to (re)set the expiry timestamp
  168. else
  169. $_SESSION['session_expires'] = time() + ($this->lifetime * 60);
  170. }
  171. } // start
  172. /**
  173. * Clears the session data but keeps the session active.
  174. *
  175. * @return void
  176. */
  177. public function clear() {
  178. foreach ( $_SESSION as $var => $val ) {
  179. unset( $_SESSION[$var] );
  180. }
  181. } // clear
  182. /**
  183. * Destroys the session including associated data.
  184. *
  185. * @return void
  186. */
  187. public function destroy() {
  188. // first unset the current session variables
  189. $this->remove_token();
  190. $this->clear();
  191. session_unset();
  192. // delete the session cookie if it exists - sets the expires time to be a year ago
  193. if( isset($_COOKIE[session_name()]) )
  194. setcookie( $this->session_name, '', time() - (60 * 60 * 24 * 365), '/' );
  195. // remove all traces of the session from the server
  196. session_destroy();
  197. // update session parameters
  198. $this->session_name = session_name();
  199. $this->session_id = session_id();
  200. } // destroy
  201. /**
  202. * Restarts the session.
  203. * Session data is cleared and a new session id is generated.
  204. *
  205. * @return void
  206. */
  207. public function restart() {
  208. // first unset the current session variables
  209. $this->clear();
  210. session_unset();
  211. // now generate a new session id
  212. session_regenerate_id();
  213. // update session parameters
  214. $this->session_name = session_name();
  215. $this->session_id = session_id();
  216. } // destroy
  217. /**
  218. * Sets a secure token against the session.
  219. * This is an additional security measure in addition to the session id and the USER_AGENT header check.
  220. * It means that even if an attacker managed to guess the session id of an authenticated user
  221. * AND managed to send the correct USER_AGENT header, they'd also have to send the correct token as well.<br /><br />
  222. * The secure token is only used for authenticated session (ie the user has already logged in),
  223. * which means attackers won't even know they need to send it unless they've successfully authenticated
  224. * a user, or they've examined the cookies stored on a client machine that has an authenticated session.
  225. *
  226. * @return boolean
  227. */
  228. public function set_token() {
  229. $fingerprint = SPF::hash();
  230. $_SESSION['session_token'] = $fingerprint;
  231. setcookie( 'session_token', $fingerprint );
  232. } // set_fingerprint
  233. /**
  234. * Removes the secure session token.
  235. *
  236. * @return void
  237. */
  238. public function remove_token() {
  239. unset($_SESSION['session_token']);
  240. setcookie( 'session_token', '', time() - 42000, '/' );
  241. } // remove_token
  242. /**
  243. * Determines whether the session has a secure token set.
  244. *
  245. * @return boolean
  246. */
  247. public function has_token() {
  248. return isset($_SESSION['session_token']);
  249. } // has_token
  250. /**
  251. * Verfies that the secure token held by the client matches the one stored in the session.
  252. * If the token has not been set this function will return true.
  253. *
  254. * @return boolean
  255. */
  256. public function check_token() {
  257. if( $this->has_token() )
  258. return isset($_COOKIE['session_token']) && $_COOKIE['session_token'] = $_SESSION['session_token'];
  259. else
  260. return true;
  261. } // check_token
  262. } // SPF_Session
  263. /**
  264. * Thrown when a session is invalid.
  265. *
  266. * @package spf
  267. * @author Simon Downes <simon@simondownes.co.uk>
  268. * @copyright Copyright (c) 2007, Simon Downes
  269. * @license http://www.opensource.org/licenses/mit-license.php
  270. */
  271. class SPF_Session_Exception extends SPF_Exception {
  272. public function __construct( $message ) {
  273. parent::__construct( $message );
  274. }
  275. } // SPF_Session_Exception
  276. ?>