PageRenderTime 70ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/SecureSession.php

http://github.com/ezimuel/PHP-Secure-Session
PHP | 215 lines | 119 code | 2 blank | 94 comment | 10 complexity | 173bdebcf58d10688d7b51fa2ef34ed6 MD5 | raw file
  1. <?php
  2. /**
  3. * ------------------------------------------------
  4. * Encrypt PHP session data using files
  5. * ------------------------------------------------
  6. * The encryption is built using mcrypt extension
  7. * and the randomness is managed by openssl
  8. * The default encryption algorithm is AES (Rijndael-128)
  9. * and we use CBC+HMAC (Encrypt-then-mac) with SHA-256
  10. *
  11. * @author Enrico Zimuel (enrico@zimuel.it)
  12. * @copyright GNU General Public License
  13. */
  14. class SecureSession {
  15. /**
  16. * Encryption algorithm
  17. *
  18. * @var string
  19. */
  20. protected $_algo= MCRYPT_RIJNDAEL_128;
  21. /**
  22. * Key for encryption/decryption
  23. *
  24. * @var string
  25. */
  26. protected $_key;
  27. /**
  28. * Key for HMAC authentication
  29. *
  30. * @var string
  31. */
  32. protected $_auth;
  33. /**
  34. * Path of the session file
  35. *
  36. * @var string
  37. */
  38. protected $_path;
  39. /**
  40. * Session name (optional)
  41. *
  42. * @var string
  43. */
  44. protected $_name;
  45. /**
  46. * Size of the IV vector for encryption
  47. *
  48. * @var integer
  49. */
  50. protected $_ivSize;
  51. /**
  52. * Cookie variable name of the encryption + auth key
  53. *
  54. * @var string
  55. */
  56. protected $_keyName;
  57. /**
  58. * Generate a random key using openssl
  59. * fallback to mcrypt_create_iv
  60. *
  61. * @param integer $length
  62. * @return string
  63. */
  64. protected function _randomKey($length=32) {
  65. if(function_exists('openssl_random_pseudo_bytes')) {
  66. $rnd = openssl_random_pseudo_bytes($length, $strong);
  67. if ($strong === true) {
  68. return $rnd;
  69. }
  70. }
  71. if (defined(MCRYPT_DEV_URANDOM)) {
  72. return mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
  73. } else {
  74. throw new Exception("I cannot generate a secure pseudo-random key. Please install OpenSSL or Mcrypt extension");
  75. }
  76. }
  77. /**
  78. * Constructor
  79. */
  80. public function __construct()
  81. {
  82. session_set_save_handler(
  83. array($this, "open"),
  84. array($this, "close"),
  85. array($this, "read"),
  86. array($this, "write"),
  87. array($this, "destroy"),
  88. array($this, "gc")
  89. );
  90. }
  91. /**
  92. * Open the session
  93. *
  94. * @param string $save_path
  95. * @param string $session_name
  96. * @return bool
  97. */
  98. public function open($save_path, $session_name)
  99. {
  100. $this->_path = $save_path.'/';
  101. $this->_name = $session_name;
  102. $this->_keyName = "KEY_$session_name";
  103. $this->_ivSize = mcrypt_get_iv_size($this->_algo, MCRYPT_MODE_CBC);
  104. if (empty($_COOKIE[$this->_keyName]) || strpos($_COOKIE[$this->_keyName],':')===false) {
  105. $keyLength = mcrypt_get_key_size($this->_algo, MCRYPT_MODE_CBC);
  106. $this->_key = self::_randomKey($keyLength);
  107. $this->_auth = self::_randomKey(32);
  108. $cookie_param = session_get_cookie_params();
  109. setcookie(
  110. $this->_keyName,
  111. base64_encode($this->_key) . ':' . base64_encode($this->_auth),
  112. $cookie_param['lifetime'],
  113. $cookie_param['path'],
  114. $cookie_param['domain'],
  115. $cookie_param['secure'],
  116. $cookie_param['httponly']
  117. );
  118. } else {
  119. list ($this->_key, $this->_auth) = explode (':',$_COOKIE[$this->_keyName]);
  120. $this->_key = base64_decode($this->_key);
  121. $this->_auth = base64_decode($this->_auth);
  122. }
  123. return true;
  124. }
  125. /**
  126. * Close the session
  127. *
  128. * @return bool
  129. */
  130. public function close()
  131. {
  132. return true;
  133. }
  134. /**
  135. * Read and decrypt the session
  136. *
  137. * @param integer $id
  138. * @return string
  139. */
  140. public function read($id)
  141. {
  142. $sess_file = $this->_path.$this->_name."_$id";
  143. if (!file_exists($sess_file)) {
  144. return false;
  145. }
  146. $data = file_get_contents($sess_file);
  147. list($hmac, $iv, $encrypted)= explode(':',$data);
  148. $iv = base64_decode($iv);
  149. $encrypted = base64_decode($encrypted);
  150. $newHmac = hash_hmac('sha256', $iv . $this->_algo . $encrypted, $this->_auth);
  151. if ($hmac !== $newHmac) {
  152. return false;
  153. }
  154. $decrypt = mcrypt_decrypt(
  155. $this->_algo,
  156. $this->_key,
  157. $encrypted,
  158. MCRYPT_MODE_CBC,
  159. $iv
  160. );
  161. return rtrim($decrypt, "\0");
  162. }
  163. /**
  164. * Encrypt and write the session
  165. *
  166. * @param integer $id
  167. * @param string $data
  168. * @return bool
  169. */
  170. public function write($id, $data)
  171. {
  172. $sess_file = $this->_path . $this->_name . "_$id";
  173. $iv = mcrypt_create_iv($this->_ivSize, MCRYPT_DEV_URANDOM);
  174. $encrypted = mcrypt_encrypt(
  175. $this->_algo,
  176. $this->_key,
  177. $data,
  178. MCRYPT_MODE_CBC,
  179. $iv
  180. );
  181. $hmac = hash_hmac('sha256', $iv . $this->_algo . $encrypted, $this->_auth);
  182. $bytes = file_put_contents($sess_file, $hmac . ':' . base64_encode($iv) . ':' . base64_encode($encrypted));
  183. return ($bytes !== false);
  184. }
  185. /**
  186. * Destoroy the session
  187. *
  188. * @param int $id
  189. * @return bool
  190. */
  191. public function destroy($id)
  192. {
  193. $sess_file = $this->_path . $this->_name . "_$id";
  194. setcookie ($this->_keyName, '', time() - 3600);
  195. return(@unlink($sess_file));
  196. }
  197. /**
  198. * Garbage Collector
  199. *
  200. * @param int $max
  201. * @return bool
  202. */
  203. public function gc($max)
  204. {
  205. foreach (glob($this->_path . $this->_name . '_*') as $filename) {
  206. if (filemtime($filename) + $max < time()) {
  207. @unlink($filename);
  208. }
  209. }
  210. return true;
  211. }
  212. }
  213. new SecureSession();