_token_name = 'token_acp'; } if (session_id()) { session_unset(); session_destroy(); $_SESSION = array(); } //Get all the ini settings to save time later $ini = ini_get_all(null, false); if($GLOBALS['config']->has('config', 'session_save_handler')) { $this->_save_handler = $GLOBALS['config']->get('config', 'session_save_handler'); } else { $this->_save_handler = Cache::getInstance()->session_save_handler(); } $this->_save_path = Cache::getInstance()->session_save_path(); ini_set('session.save_handler', $this->_save_handler); if($this->_save_handler!=='files') { ini_set('session.save_path', $this->_save_path); } if ($ini['session.use_trans_sid'] != '0') { //disable transparent sid support ini_set('session.use_trans_sid', '0'); } if ($ini['session.gc_probability'] != 15) { //Clean up 15% of the time ini_set('session.gc_probability', 15); } if ($ini['session.gc_divisor'] != 100) { ini_set('session.gc_divisor', 100); } $cookie_domain = ltrim($GLOBALS['config']->get('config', 'cookie_domain'), '.'); if (!empty($cookie_domain) && strstr($GLOBALS['storeURL'], $cookie_domain) && strpos($cookie_domain, '.')) { $this->_session_domain = '.'.$cookie_domain; ini_set('session.cookie_domain', $this->_session_domain); } $this->_session_path = $GLOBALS['rootRel'] == '/' ? $GLOBALS['rootRel'] : substr($GLOBALS['rootRel'],0,-1); ini_set('session.cookie_path', $this->_session_path); //If the current session time is longer we will not change anything if ($ini['session.gc_maxlifetime'] < $this->_session_timeout) { ini_set('session.gc_maxlifetime', $this->_session_timeout); } if ($ini['session.cookie_lifetime'] < $this->_session_timeout) { ini_set('session.cookie_lifetime', $this->_session_timeout); } if (!$ini['session.use_cookies']) { //Enforce cookies only ini_set('session.use_cookies', true); } if (!$ini['session.use_only_cookies']) { // make sure session is cookie based only ini_set('session.use_only_cookies', true); } if (!$ini['session.cookie_httponly']) { // make sure session cookies are http ONLY! ini_set('session.cookie_httponly', true); } if (CC_SSL && empty($ini['session.cookie_samesite'])) { // make sure session cookies are samesite ini_set('session.cookie_samesite', 'None'); } if (!$ini['session.cookie_secure'] && CC_SSL) { // make sure session cookies are secure if SSL is enabled ini_set('session.cookie_secure', true); } $this->_start(); $this->_validate(); $this->_setTimers(); } public function __destruct() { //Close this session $this->_close(); } /** * Setup the instance (singleton) * * @return Session */ public static function getInstance() { if (!(self::$_instance instanceof self)) { self::$_instance = new self(); } return self::$_instance; } //=====[ Public ]======================================= /** * Is a user blocked * * @return bool */ public function blocked() { return $this->_user_blocked; } /** * Block a user * * @param string $user * @param bool $login * @param string $location * @param int $attempts * @param int $time */ public function blocker($user, $user_id, $login = false, $location = false, $attempts = 5, $time = 600) { $now = time(); // Access Log $record = array( 'type' => $location, 'time' => $now, 'username' => (!empty($user)) ? $user : '--', 'user_id' => $user_id, 'ip_address'=> get_ip_address(), 'useragent' => $this->_http_user_agent(), 'success' => ($login) ? 'Y' : 'N', ); $log_days = $GLOBALS['config']->get('config', 'r_staff'); if (ctype_digit((string)$log_days) && $log_days > 0) { $GLOBALS['db']->insert('CubeCart_access_log', $record); $GLOBALS['db']->delete('CubeCart_access_log', 'time < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL '.$log_days.' DAY))'); } elseif (empty($log_days) || !$log_days) { $GLOBALS['db']->insert('CubeCart_access_log', $record); } // Remove expired blocks $GLOBALS['db']->delete('CubeCart_blocker', array('last_attempt' => '<='.($now - $time))); // Search for active blocks $where = array( 'user_agent' => $this->_http_user_agent(), 'ip_address' => get_ip_address(), 'location' => $location, ); $blacklist = $GLOBALS['db']->select('CubeCart_blocker', array('block_id', 'ban_expires', 'last_attempt', 'level'), $where); if ($blacklist) { $blocked = $blacklist[0]; if ((int)$blocked['level'] == (int)$attempts) { // Ban level reached if ((int)$blocked['ban_expires'] <= $now) { // Ban expired - Allowed $GLOBALS['db']->delete('CubeCart_blocker', array('block_id' => $blocked['block_id'])); } else { // Still banned - Denied $this->_user_blocked = true; } } elseif (!$login) { // Attempts remaining $record = array( 'last_attempt' => $now, 'level' => ($blocked['last_attempt'] <= ($now - $time)) ? 1 : $blocked['level'] + 1, ); if ($record['level'] == $attempts) { // Blocked $record['ban_expires'] = ($now+$time); $this->_user_blocked = true; } $GLOBALS['db']->update('CubeCart_blocker', $record, array('block_id' => $blocked['block_id'])); } } elseif (!$login) { // Login failed - Create blacklist entry $record = array( 'level' => 1, 'last_attempt' => $now, 'ban_expires' => 0, 'username' => strip_tags($user), 'location' => $location, 'user_agent' => $this->_http_user_agent(), 'ip_address' => get_ip_address(), ); $GLOBALS['db']->insert('CubeCart_blocker', $record); } return (bool)$this->_user_blocked; } /** * Check a form token * * @param string $token * @return bool */ public function checkToken($token) { // Continue without error if no security token is set if (!$this->get($this->_token_name)) { return true; } return ($this->get($this->_token_name) == $token); } /** * Have cookied been accepted or not * * Deprecated but left for backward compatibility * * @param string $token * @return bool */ public function cookiesBlocked() { // Check cookies exists for verified and if so return value if (isset($_COOKIE['accept_cookies']) && $_COOKIE['accept_cookies']=='false') { return false; } elseif (!$GLOBALS['config']->get('config', 'cookie_dialogue')) { return false; } if ($GLOBALS['db']->select('CubeCart_geo_country', false, array('numcode' => $GLOBALS['config']->get('config', 'store_country'), 'eu' => '1')) !== false) { return true; } else { return false; } } /** * Delete something from the session * * @param string $name * @param string $namespace * @return bool */ public function delete($name, $namespace = 'system') { $namespace = $this->_namespace($namespace); //If the session isn't active we don't need to continue if ($this->_state != 'active') { return true; } if (!isset($_SESSION[$namespace])) { return false; } //If there is not a name if (empty($name)) { //Remove the entire namespace unset($_SESSION[$namespace]); return true; } elseif (isset($_SESSION[$namespace][$name])) { //Remove just the element unset($_SESSION[$namespace][$name]); return true; } return false; } /** * Destroy session * * @return bool */ public function destroy() { if ($this->_state == 'destroyed') { return true; } //Delete the session from the DB $GLOBALS['db']->delete('CubeCart_sessions', array('session_id' => $this->getId()), false); //Completely unset everything $_SESSION = array(); //Kill the cookies if (isset($_COOKIE[session_name()])) { $this->set_cookie(session_name(), '', time() - 42000); unset($_COOKIE[session_name()]); } //Destory it session_unset(); session_destroy(); $this->_state = 'destroyed'; return true; } /** * Get data from the session * * If name is empty the entire name space will be returned * * @param string $name * @param string $namespace * @param string $default */ public function get($name, $namespace = 'system', $default = false) { $namespace = $this->_namespace($namespace); if ($this->_state != 'active' && $this->_state != 'expired') { return $default; } if (isset($_SESSION[$namespace])) { if (!empty($name) && isset($_SESSION[$namespace][$name])) { return $_SESSION[$namespace][$name]; } elseif (empty($name) && !empty($_SESSION[$namespace])) { return $_SESSION[$namespace]; } } return $default; } /** * Get session id * * @return string */ public function getId() { if ($this->_state == 'destroyed') { return null; } return session_id(); } /** * Get session name * * @return string The session name */ public function getName() { if ($this->_state == 'destroyed') { return null; } return session_name(); } /** * Get the session state * * @return string */ public function getState() { return $this->_state; } /** * Get session data from database * * @return false/array/string */ public function getSessionTableData($column = false) { $data = $GLOBALS['db']->select('CubeCart_sessions', $column, array('session_id' => $this->getId()), false, 1, false, false); if (is_array($data)) { if (count($data[0])==1 && is_string($column)) { return $data[0][$column]; } else { return $data[0]; } } return false; } /** * Create a session token to help prevent CSRF * * @param bool $new If true, force a new token to be created * @return string The session token */ public function getToken($new = false) { if ((($token = $this->get($this->_token_name)) === false) || $new) { $token = $this->_createToken(); $this->set($this->_token_name, $token); } return $token; } /** * Does the session have something * * @param string $name * @param string $namespace * @return bool */ public function has($name, $namespace = 'system') { $namespace = $this->_namespace($namespace); if ($this->_state != 'active') { return false; } if (!isset($_SESSION[$namespace])) { return false; } if (empty($name)) { return true; } else { return isset($_SESSION[$namespace][$name]); } } /** * Is an element empty * * @param string $config_name * @param string $element * @return bool */ public function isEmpty($name, $namespace) { //If the element isn't there then it is empty if (!$this->has($name, $namespace)) { return true; } $namespace = $this->_namespace($namespace); return empty($_SESSION[$namespace][$name]); } public function regenerateSessionId() { $old_session = $this->getId(); session_regenerate_id(); Database::getInstance()->update('CubeCart_sessions', array('session_id' => $this->getId()), array('session_id' => $old_session), false); $this->set_cookie(session_name(), session_id(), time()+$this->_session_timeout); } /** * Set a session value to something * * @param string $name * @param string $value * @param string $namespace * @param bool $overwrite * @return bool */ public function set($name, $value, $namespace = 'system', $overwrite = false) { $namespace = $this->_namespace($namespace); if ($this->_state != 'active') { return true; } if (is_null($value)) { unset($_SESSION[$namespace][$name]); } else { if (empty($name)) { if (!is_array($value)) { $_SESSION[$namespace] = $value; } else { if (isset($_SESSION[$namespace]) && !$overwrite) { $_SESSION[$namespace] = merge_array($_SESSION[$namespace], $value); } else { $_SESSION[$namespace] = $value; } } } else { if (!is_array($value)) { $_SESSION[$namespace][$name] = $value; } else { if (isset($_SESSION[$namespace][$name]) && !$overwrite) { $_SESSION[$namespace][$name] = merge_array($_SESSION[$namespace][$name], $value); } else { $_SESSION[$namespace][$name] = $value; } } } } return true; } /** * Set a page back to the session */ public function setBack() { if (isset($_SERVER['HTTP_REFERER']) && !empty($_SERVER['HTTP_REFERER'])) { //Make sure the referer is local and not the login if (substr($_SERVER['HTTP_REFERER'], 0, strlen(CC_STORE_URL)) == CC_STORE_URL && $_SERVER['HTTP_REFERER'] != CC_STORE_URL.'index.php?_a=login') { $this->set('back', $_SERVER['HTTP_REFERER']); } } } /** * Set cookie * * @param string $name * @param string $value * @param integer $expire * @return bool */ public function set_cookie($name, $value, $expires, $options = array()) { $params = session_get_cookie_params(); $params = array_merge($params, $options); // Allow overwrite for specific cookies $date = new Datetime(); $date->setTimestamp($expires); $attributes = ''; $attributes .= ';Expires='.$date->format(DateTime::COOKIE); $attributes .= ';Domain='.$this->_session_domain; $attributes .= ';Path='.$this->_session_path; if(CC_SSL) { $attributes .= ';SameSite='.$params['samesite']; $attributes .= ';Secure'; } if($params['httponly']) { $attributes .= ';HttpOnly'; } // Ref: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie header('Set-Cookie: '.$name.'='.$value.$attributes); } //=====[ Private ]======================================= /** * Close a session * * @return true */ private function _close() { if ($this->_state == 'closed') { return true; } $record = array( 'location' => currentPage() . (strpos(currentPage(),"_a=404")!==false ? "
".$_SERVER['REQUEST_URI']."" : ""), 'session_last' => $this->get('session_last', 'client', ''), 'acp' => ADMIN_CP ); //Use the instance because the global might be gone already Database::getInstance()->update('CubeCart_sessions', $record, array('session_id' => $this->getId()), false); // Tidy Access Logs keep months worth Database::getInstance()->delete('CubeCart_access_log', array('time' => '<'.(time()-(3600*24*7*4)))); // Purge sessions older than the session time out Database::getInstance()->delete('CubeCart_sessions', array('session_last' => '<='.(time() - $this->_session_timeout)), false); $this->_state = 'closed'; session_write_close(); return true; } /** * Create a form token * * @return string */ private function _createToken() { return md5(session_name().time().mt_rand(0, mt_getrandmax())); } /** * User agent * * @return string */ private function _http_user_agent() { return strpos($_SERVER['HTTP_USER_AGENT'], 'Trident') ? 'IEX' : htmlspecialchars($_SERVER['HTTP_USER_AGENT']); } /** * Check & build the namespace * * @param string $namespace * @return string */ private function _namespace($namespace) { if ($namespace[0] == '_') { trigger_error('Session namespace cannot start with _', E_USER_ERROR); } return '__'.$namespace; } /** * Setup session timers */ private function _setTimers() { if (!$this->has('session_start', 'client')) { $start = time(); $this->set('session_start', $start, 'client'); $this->set('session_last', $start, 'client'); } else { $this->set('session_start', $this->get('session_last', 'client'), 'client'); $this->set('session_last', time(), 'client'); } } /** * Start session */ private function _start() { if($this->_save_handler!=='files') { session_save_path($this->_save_path); } else { $session_save_path = $GLOBALS['config']->get('config', 'session_save_path'); if (!empty($session_save_path) && file_exists($session_save_path)) { session_save_path($session_save_path); } } session_cache_limiter('nocache'); $session_prefix = CC_SSL ? 'S' : ''; session_name('CC'.$session_prefix.'_'.strtoupper(substr(md5(CC_ROOT_DIR), 0, 10))); session_start(); // Increase session length on each page load. if (isset($_COOKIE[session_name()])) { $this->set_cookie(session_name(), session_id(), time()+$this->_session_timeout); } } /** * Validate session * * @param bool $restart */ private function _validate() { $ip = get_ip_address(); if (($current = $GLOBALS['db']->select('CubeCart_sessions', false, array('session_id' => $this->getId()), false, 1, false, false)) === false) { $record = array( 'admin_id' => 0, 'customer_id' => 0, 'ip_address' => $ip, 'location' => '', 'session_id' => $this->getId(), 'session_last' => time(), 'session_start' => time(), 'useragent' => $this->_http_user_agent(), 'acp' => ADMIN_CP ); $GLOBALS['db']->insert('CubeCart_sessions', $record, false); $this->set('ip_address', $ip, 'client'); $this->set('useragent', $this->_http_user_agent(), 'client'); } else { $this->session_data = $current[0]; $this->set('ip_address', $current[0]['ip_address'], 'client'); $this->set('useragent', $current[0]['useragent'], 'client'); } } }