Last active
September 29, 2015 00:08
-
-
Save jligerofleitas/5d9521ac5dcd86cc4834 to your computer and use it in GitHub Desktop.
Revisions
-
jligerofleitas revised this gist
Sep 28, 2015 . 1 changed file with 3 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -42,6 +42,9 @@ `data` blob NOT NULL, `updated` datetime NOT NULL ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; * * ATENTION: * This is not fully tested, just a work in progress; use on your own risk ;) */ class SecureSession -
jligerofleitas revised this gist
Sep 28, 2015 . No changes.There are no files selected for viewing
-
jligerofleitas revised this gist
Sep 28, 2015 . No changes.There are no files selected for viewing
-
jligerofleitas created this gist
Sep 28, 2015 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,480 @@ <?php /** * ------------------------------------------------ * Encrypt PHP session data using files * ------------------------------------------------ * The encryption is built using mcrypt extension * and the randomness is managed by openssl * The default encryption algorithm is AES (Rijndael-128) * and we use CBC+HMAC (Encrypt-then-mac) with SHA-256 * * @author Enrico Zimuel (enrico@zimuel.it) * @copyright GNU General Public License * @GitHub https://github.com/ezimuel/PHP-Secure-Session * * Some modifications added to allow filesystem AND database session using PDO. * Convinience set(), get() and flash() methods. * * Filesystem - default behavior * $session = new SecureSession(); * * Database: * $session = new SecureSession('db', array( * 'dsn' => 'mysql:host=localhost;dbname=testdb', * 'user' => 'root', * 'password' => '123', * )); * * Usage: * - Just create a new SessionSecure instance: $session = new SecureSession(); * - $session->get('session_key') to retrieve stored session data * - $session->set('session_key', 'session_value') to set session data; * - ALL DATA IS SERIALIZED BEFORE IT IS SAVED IN $_SESSION, so you can save almost whatever you want. * - Besides that, when stored in DB, it is also base64 encoded to ensure binary correctness. * - flash messages, $session->flash('flash_key', 'flash_value') to set a flash message, and $session->flash('flash_key') to retrieve. * * * Table definition to store sessions (MySQL): CREATE TABLE IF NOT EXISTS `session` ( `id` int(11) NOT NULL, `sessionId` varchar(255) NOT NULL, `data` blob NOT NULL, `updated` datetime NOT NULL ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; */ class SecureSession { /** * Encryption algorithm * * @var string */ protected $_algo= MCRYPT_RIJNDAEL_128; /** * Key for encryption/decryption * * @var string */ protected $_key; /** * Key for HMAC authentication * * @var string */ protected $_auth; /** * Path of the session file * * @var string */ protected $_path; /** * Session name (optional) * * @var string */ protected $_name; /** * Size of the IV vector for encryption * * @var integer */ protected $_ivSize; /** * Cookie variable name of the encryption + auth key * * @var string */ protected $_keyName; /** * Database PDO conn */ private $conn; /** * Generate a random key using openssl * fallback to mcrypt_create_iv * * @param integer $length * @return string */ protected function _randomKey($length=32) { if(function_exists('openssl_random_pseudo_bytes')) { $rnd = openssl_random_pseudo_bytes($length, $strong); if ($strong === true) { return $rnd; } } return mcrypt_create_iv($length, MCRYPT_DEV_URANDOM); } /** * Constructor */ public function __construct($type = 'file', $conn = array()) { if ( ! extension_loaded('mcrypt')) { throw new Exception("The SecureSession class needs the Mcrypt PHP " . "extension, please install it."); } if ($type === 'db'): session_set_save_handler( array($this, "open"), array($this, "close"), array($this, "db_read"), array($this, "db_write"), array($this, "db_destroy"), array($this, "db_gc") ); try { $this->conn = new \PDO($conn['dsn'], $conn['user'], $conn['password']); $this->conn->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); $this->conn->setAttribute(\PDO::ATTR_EMULATE_PREPARES, false); } catch(\PDOException $e) { throw new \PDOException("SecureSession could not connect " . "to database with provided parameters " . "({$e->getMessage()})"); } else: session_set_save_handler( array($this, "open"), array($this, "close"), array($this, "read"), array($this, "write"), array($this, "destroy"), array($this, "gc") ); endif; // TODO: kind of "autostart" session_start(); } /** * FILE HANDLER */ /** * Open the session * * @param string $save_path * @param string $session_name * @return bool */ public function open($save_path, $session_name) { $this->_path = $save_path.'/'; $this->_name = $session_name; $this->_keyName = "KEY_$session_name"; $this->_ivSize = mcrypt_get_iv_size($this->_algo, MCRYPT_MODE_CBC); $_keyName = (isset($_COOKIE[$this->_keyName]))? $_COOKIE[$this->_keyName]:''; if (empty($_keyName) || strpos($_keyName,':')===false) { $keyLength = mcrypt_get_key_size($this->_algo, MCRYPT_MODE_CBC); $this->_key = self::_randomKey($keyLength); $this->_auth = self::_randomKey(32); $cookie_param = session_get_cookie_params(); setcookie( $this->_keyName, base64_encode($this->_key) . ':' . base64_encode($this->_auth), // if session cookie lifetime > 0 then add to current time; otherwise leave it as zero, honoring zero's special meaning: expire at browser close. ($cookie_param['lifetime'] > 0) ? time() + $cookie_param['lifetime'] : 0, $cookie_param['path'], $cookie_param['domain'], $cookie_param['secure'], $cookie_param['httponly'] ); } else { list ($this->_key, $this->_auth) = explode (':',$_keyName); $this->_key = base64_decode($this->_key); $this->_auth = base64_decode($this->_auth); } return true; } /** * Close the session * * @return bool */ public function close() { \session_write_close(); return true; } /** * Read and decrypt the session * * @param integer $id * @return string */ public function read($id) { $sess_file = $this->_path.$this->_name."_$id"; if ( ! file_exists($sess_file)) { return false; } $data = file_get_contents($sess_file); list($hmac, $iv, $encrypted)= explode(':',$data); $iv = base64_decode($iv); $encrypted = base64_decode($encrypted); $newHmac = hash_hmac('sha256', $iv . $this->_algo . $encrypted, $this->_auth); if ($hmac !== $newHmac) { return false; } $decrypt = mcrypt_decrypt( $this->_algo, $this->_key, $encrypted, MCRYPT_MODE_CBC, $iv ); return rtrim($decrypt, "\0"); } /** * Encrypt and write the session * * @param integer $id * @param string $data * @return bool */ public function write($id, $data) { $sess_file = $this->_path . $this->_name . "_$id"; $iv = mcrypt_create_iv($this->_ivSize, MCRYPT_DEV_URANDOM); $encrypted = mcrypt_encrypt( $this->_algo, $this->_key, $data, MCRYPT_MODE_CBC, $iv ); $hmac = hash_hmac('sha256', $iv . $this->_algo . $encrypted, $this->_auth); $bytes = file_put_contents($sess_file, $hmac . ':' . base64_encode($iv) . ':' . base64_encode($encrypted)); return ($bytes !== false); } /** * Destoroy the session * * @param int $id * @return bool */ public function destroy($id) { \session_write_close(); $sess_file = $this->_path . $this->_name . "_$id"; setcookie ($this->_keyName, '', time() - 3600); return(@unlink($sess_file)); } /** * Garbage Collector * * @param int $max * @return bool */ public function gc($max) { foreach (glob($this->_path . $this->_name . '_*') as $filename) { if (filemtime($filename) + $max < time()) { @unlink($filename); } } return true; } /** * Just checks if session has been started. */ public function session_start() { if (\function_exists('session_status')): if (\session_status() === PHP_SESSION_NONE): \session_start(); endif; elseif (\session_id() == ''): \session_start(); endif; } public function get($key = '') { $this->session_start(); if (isset($_SESSION[$key])) { return \unserialize($_SESSION[$key]); } else { return false; } } public function set($key, $value = '') { $this->session_start(); $_SESSION[$key] = \serialize($value); \session_write_close(); } public function delete($key) { $this->session_start(); unset($_SESSION[$key]); \session_write_close(); } /** * Flash messages * * http://www.phpdevtips.com/2013/05/simple-session-based-flash-messages/ * * @param type $key * @param type $value */ public function flash($key = '', $value = '') { $this->session_start(); if (empty($key)) { return array(); } // Flash message does not exists: create it if ( ! empty($value) && empty($_SESSION[$key])) { if ( ! empty($_SESSION[$key])) { unset($_SESSION[$key]); } $_SESSION[$key] = serialize($value); \session_write_close(); return true; } // Flash message exists: return it elseif ( ! empty($_SESSION[$key]) && empty($value)) { $value = $_SESSION[$key]; $flash = unserialize($value); unset($_SESSION[$key]); \session_write_close(); return $flash; } // Just in case... else { return array(); } } /*************************************************************************** * DATABASE HANDLER */ /** * Read and decrypt the session * * @param integer $id * @return string */ public function db_read($id) { // Does session exist? $session = $this->conn->prepare("SELECT data FROM session WHERE sessionId=:session_id"); $session->execute(array(':session_id' => $id)); $row = $session->fetch(); // Row with session ID already exists if ((bool)$row): $data = (\base64_decode($row['data'])); list($hmac, $iv, $encrypted)= explode(':',$data); $iv = base64_decode($iv); $encrypted = base64_decode($encrypted); $newHmac = hash_hmac('sha256', $iv . $this->_algo . $encrypted, $this->_auth); if ($hmac !== $newHmac) { return false; } $decrypt = mcrypt_decrypt( $this->_algo, $this->_key, $encrypted, MCRYPT_MODE_CBC, $iv ); return rtrim($decrypt, "\0"); // No session in DB: create row with session ID else: $now = \date("Y-m-d H:i:s"); $query = "INSERT INTO session (sessionId, updated) VALUES ('$id', '$now');"; $session = $this->conn->query($query); return (bool)$session->rowCount(); endif; } /** * Encrypt and write the session * * @param integer $id * @param string $data * @return bool */ public function db_write($id, $data) { $iv = mcrypt_create_iv($this->_ivSize, MCRYPT_DEV_URANDOM); $encrypted = mcrypt_encrypt( $this->_algo, $this->_key, $data, MCRYPT_MODE_CBC, $iv ); $hmac = hash_hmac('sha256', $iv . $this->_algo . $encrypted, $this->_auth); $session = $this->conn->prepare("UPDATE session SET data=:data, updated=:updated WHERE sessionId=:session_id"); $row_data = \base64_encode(($hmac.':'.\base64_encode($iv).':'.\base64_encode($encrypted))); $session->execute(array(':data' => $row_data, ':updated' => date("Y-m-d H:i:s"), ':session_id' => $id, )); return (bool)$session->rowCount(); } /** * Destroy the session * * @param int $id * @return bool */ public function db_destroy($id) { \session_write_close(); $session = $this->conn->prepare("DELETE FROM session WHERE sessionId=:session_id"); $session->execute(array(':session_id' => $id)); return (bool)$session->rowCount(); } /** * Garbage Collector. * Delete rows from database. * * @param int $max Seconds of inactive session * @return bool */ public function db_gc($max) { $session = $this->conn->prepare("DELETE FROM session WHERE updated < :date"); $date = date("Y-m-d H:i:s", time() - $max); $session->execute(array(':date' => $date)); return true; } }