/** * Constructor of class. Initializes the class and automatically calls * {@link http://php.net/manual/en/function.session-start.php start_session()}. * * <code> * // first, connect to a database containing the sessions table * * // include the class (use the composer-"autoloader") * require 'vendor/autoload.php'; * * // start the session * $session = new Session2DB(); * </code> * * By default, the cookie used by PHP to propagate session data across multiple pages ('PHPSESSID') uses the * current top-level domain and subdomain in the cookie declaration. * * Example: www.domain.com * * This means that the session data is not available to other subdomains. Therefore, a session started on * www.domain.com will not be available on blog.domain.com. The solution is to change the domain PHP uses when it * sets the 'PHPSESSID' cookie by calling the line below *before* instantiating the Session library. * * <code> * // takes the domain and removes the subdomain * // blog.domain.com becoming .domain.com * ini_set( * 'session.cookie_domain', * substr($_SERVER['SERVER_NAME'], strpos($_SERVER['SERVER_NAME'], '.')) * ); * </code> * * From now on whenever PHP sets the 'PHPSESSID' cookie, the cookie will be available to all subdomains! * * @param string $security_code (Optional) The value of this argument is appended to the string created * by * concatenating the user's User Agent (browser) string (or an empty string * if "lock_to_user_agent" is FALSE) and to the user's IP address (or an * empty string if "lock_to_ip" is FALSE), before creating an MD5 hash out * of it and storing it in the database. * * On each call this value will be generated again and compared to the * value stored in the database ensuring that the session is correctly * linked * with the user who initiated the session thus preventing session * hijacking. * * <samp>To prevent session hijacking, make sure you choose a string around * 12 characters long containing upper- and lowercase letters, as well as * digits. To simplify the process, use {@link * https://www.random.org/passwords/?num=1&len=12&format=html&rnd=new this} * link to generate such a random string.</samp> * * @param int|mixed $session_lifetime (Optional) The number of seconds after which a session will be considered * as <i>expired</i>. * * Expired sessions are cleaned up from the database whenever the <i>garbage * collection routine</i> is run. The probability of the <i>garbage * collection routine</i> to be executed is given by the values of * <i>$gc_probability</i> and <i>$gc_divisor</i>. See below. * * Default is the value of <i>session.gc_maxlifetime</i> as set in in * php.ini. Read more at {@link * http://www.php.net/manual/en/session.configuration.php} * * To clear any confusions that may arise: in reality, * <i>session.gc_maxlifetime</i> does not represent a session's lifetime but * the number of seconds after which a session is seen as <i>garbage</i> and * is deleted by the <i>garbage collection routine</i>. The PHP setting that * sets a session's lifetime is * <i>session.cookie_lifetime</i> and is usually set to "0" - indicating * that * a session is active until the browser/browser tab is closed. When this * class is used, a session is active until the browser/browser tab is * closed and/or a session has been inactive for more than the number of * seconds specified by <i>session.gc_maxlifetime</i>. * * To see the actual value of <i>session.gc_maxlifetime</i> for your * environment, use the {@link get_settings()} method. * * Pass an empty string to keep default value. * * @param boolean $lock_to_user_agent (Optional) Whether to restrict the session to the same User Agent (or * browser) as when the session was first opened. * * <i>The user agent check only adds minor security, since an attacker that * hijacks the session cookie will most likely have the same user agent.</i> * * In certain scenarios involving Internet Explorer, the browser will * randomly change the user agent string from one page to the next by * automatically switching into compatibility mode. So, on the first load * you would have something like: * * <code>Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; * etc...</code> * * and reloading the page you would have * * <code> Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; * etc...</code> * * So, if the situation asks for this, change this value to FALSE. * * Default is TRUE. * * @param boolean $lock_to_ip (Optional) Whether to restrict the session to the same IP as when the * session was first opened. * * Use this with caution as many users have dynamic IP addresses which may * change over time, or may come through proxies. * * This is mostly useful if your know that all your users come from static * IPs. * * Default is FALSE. * * @param int $gc_probability (Optional) Used in conjunction with <i>$gc_divisor</i>. It defines the * probability that the <i>garbage collection routine</i> is started. * * The probability is expressed by the formula: * * <code> * $probability = $gc_probability / $gc_divisor; * </code> * * So, if <i>$gc_probability</i> is 1 and <i>$gc_divisor</i> is 100, it * means * that there is a 1% chance the the <i>garbage collection routine</i> will * be called on each request. * * Default is the value of <i>session.gc_probability</i> as set in php.ini. * Read more at {@link * http://www.php.net/manual/en/session.configuration.php} * * To see the actual value of <i>session.gc_probability</i> for your * environment, and the computed <i>probability</i>, use the * {@link get_settings()} method. * * Pass an empty string to keep default value. * * @param int $gc_divisor (Optional) Used in conjunction with <i>$gc_probability</i>. It * defines the probability that the <i>garbage collection routine</i> is * started. * * The probability is expressed by the formula: * * <code> * $probability = $gc_probability / $gc_divisor; * </code> * * So, if <i>$gc_probability</i> is 1 and <i>$gc_divisor</i> is 100, it * means * that there is a 1% chance the the <i>garbage collection routine</i> will * be called on each request. * * Default is the value of <i>session.gc_divisor</i> as set in php.ini. * Read more at {@link * http://www.php.net/manual/en/session.configuration.php} * * To see the actual value of <i>session.gc_divisor</i> for your * environment, and the computed <i>probability</i>, use the * {@link get_settings()} method. * * Pass an empty string to keep default value. * * @param string $table_name (Optional) Name of the DB table used by the class. * * Default is <i>session_data</i>. * * @param int $lock_timeout (Optional) The maximum amount of time (in seconds) for which a lock * on the session data can be kept. * * <i>This must be lower than the maximum execution time of the script!</i> * * Session locking is a way to ensure that data is correctly handled in a * scenario with multiple concurrent AJAX requests. * * Read more about it at * {@link * http://thwartedefforts.org/2006/11/11/race-conditions-with-ajax-and-php-sessions/} * * Default is <i>60</i> * */ public function __construct($security_code = '', $session_lifetime = '', $lock_to_user_agent = true, $lock_to_ip = false, $gc_probability = 1, $gc_divisor = 1000, $table_name = 'session_data', $lock_timeout = 60) { $this->db = DB::getInstance(); // Prevent session-fixation // See: http://en.wikipedia.org/wiki/Session_fixation ini_set('session.cookie_httponly', 1); ini_set('session.session.use_only_cookies', 1); // Use the SHA-1 hashing algorithm ini_set('session.hash_function', 1); // Increase character-range of the session ID to help prevent brute-force attacks ini_set('session.hash_bits_per_character', 6); // fallback for the security-code if (!$security_code || ($security_code = '###set_the_security_key###')) { $security_code = 'sEcUrmenadwork_))'; } // continue if there is an active DB connection if ($this->db->ping()) { // make sure session cookies never expire so that session lifetime // will depend only on the value of $session_lifetime ini_set('session.cookie_lifetime', 0); // if $session_lifetime is specified and is an integer number if ($session_lifetime !== '' && is_int($session_lifetime)) { ini_set('session.gc_maxlifetime', (int) $session_lifetime); } else { // fallback to 1h - 3600s ini_set('session.gc_maxlifetime', 3600); } // if $gc_probability is specified and is an integer number if ($gc_probability !== '' && is_int($gc_probability)) { ini_set('session.gc_probability', $gc_probability); } // if $gc_divisor is specified and is an integer number if ($gc_divisor !== '' && is_int($gc_divisor)) { ini_set('session.gc_divisor', $gc_divisor); } // get session lifetime $this->session_lifetime = ini_get('session.gc_maxlifetime'); // we'll use this later on in order to try to prevent HTTP_USER_AGENT spoofing $this->security_code = $security_code; // some other defaults $this->lock_to_user_agent = $lock_to_user_agent; $this->lock_to_ip = $lock_to_ip; // the table to be used by the class $this->table_name = $this->db->quote_string($table_name); // the maximum amount of time (in seconds) for which a process can lock the session $this->lock_timeout = $lock_timeout; // register the new handler session_set_save_handler(array(&$this, 'open'), array(&$this, 'close'), array(&$this, 'read'), array(&$this, 'write'), array(&$this, 'destroy'), array(&$this, 'gc')); // start the session if (PHP_SAPI !== 'cli') { session_start(); } // running from the cli doesn't set $_SESSION if (!isset($_SESSION)) { $_SESSION = array(); } // assume no flashdata $this->flashdata = array(); // if there are any flashdata variables that need to be handled if (isset($_SESSION[self::flashDataVarName])) { // store them $this->flashdata = unserialize($_SESSION[self::flashDataVarName]); // and destroy the temporary session variable unset($_SESSION[self::flashDataVarName]); } // handle flashdata after script execution register_shutdown_function(array($this, '_manage_flashdata')); // if no DB connections could be found // trigger a fatal error message and stop execution } else { trigger_error('Session: No DB-Connection!', E_USER_ERROR); } }
public function testPing() { $ping = $this->db->ping(); self::assertEquals(true, $ping); }