samesite if (!isset($this->samesite)) { $this->samesite = ($cookie_samesite_lax) ? self::SAMESITE_LAX : self::SAMESITE_STRICT; } // Set the session lifetime if (!isset($this->lifetime)) { $this->lifetime = $auth['session_php']['session_expire_time'] ?? 0; } // Start up sessions $this->init($this->lifetime); } /** * Get the session handler to use. * * If the database session handler is not available, use the ordinary PHP session handler. * * @return SessionHandlerDb|SessionHandler */ protected function getSessionHandler() { // The sessions table was only created in Upgrade 56. We test for the schema version rather than the existence of // the table, because the table is renamed in later upgrades. if (db_schema_version(db()) < 56) { return new SessionHandler(); } // The DB session handler uses locks, and because we use locks elsewhere, this means we need support for multiple // locks. We need to test now, rather than catching an exception later, because resetting the session handler // will reset the session id causing us to lose session data. if (!db()->supportsMultipleLocks()) { $message = "The database server does not support multiple locks, so the database session handler " . "cannot be used. Using ordinary PHP sessions instead."; trigger_error($message); return new SessionHandler(); } // Otherwise use the DB session handler. return new SessionHandlerDb(); } // Normally there's no need to call init() from outside the Session classes. // It only needs to be called to restart sessions, after, for example, a user // has been logged off, and you need to use session variables. public function init(int $lifetime) : void { global $auth; if (session_status() === PHP_SESSION_ACTIVE) { // We've already started sessions return; } // Session settings, for security // ini_set() only accepts string values prior to PHP 8.1.0 ini_set('session.cookie_httponly', '1'); if (version_compare(PHP_VERSION, '7.3', '>=')) { // Only introduced in PHP Version 7.3 ini_set('session.cookie_samesite', $this->samesite); } ini_set('session.cookie_secure', (is_https()) ? '1' : '0'); // More settings, as a defence against session fixation. ini_set('session.use_only_cookies', '1'); ini_set('session.use_strict_mode', '1'); ini_set('session.use_trans_sid', '0'); $cookie_path = get_cookie_path(); // We don't want the session garbage collector to delete the session before it has expired if ($lifetime !== 0) { assert(version_compare(MRBS_MIN_PHP_VERSION, '8.1') < 0, 'The strval() in the line below is no longer required.'); ini_set('session.gc_maxlifetime', strval(max(ini_get('session.gc_maxlifetime'), $lifetime))); } if (isset($auth['session_php']['session_name'])) { // call before session_set_cookie_params() - see PHP manual session_name($auth['session_php']['session_name']); } session_set_cookie_params($lifetime, $cookie_path); // Set the session handler and start up sessions try { session_set_save_handler($this->getSessionHandler(), true); if (false === session_start()) { throw new \Exception("session_start() failed"); } } catch (\Exception $e) { $message = "Could not start sessions ('" . $e->getMessage() . "')."; $message .= " Trying ordinary PHP sessions."; trigger_error($message, E_USER_WARNING); session_set_save_handler(new SessionHandler(), true); if (false === session_start()) { throw new \Exception("MRBS: could not start sessions"); } } } protected function destroy() : void { // Delete the session data encryption key cookie. If we don't do this then, when // a new session is created, unless the expiry is set to 0 (ie on browser close), // it will have a longer lifetime than the key cookie, which when it was created // was given the same lifetime as the session cookie. Once the key cookie expires, // the session handler will create a new cookie with a new key. So when the session // handler comes to decrypt the session data it will be doing so with the new key, // and not the key used to encrypt it. This will result in the Crypto library // throwing a WrongKeyOrModifiedCiphertextException with the message "Integrity // check failed". // // This needs to be done before the session is destroyed, otherwise the // deleteKeyCookie method won't be able to get the session name (which it needs // in order to delete the key cookie). SessionHandlerDb::deleteKeyCookie(); // Unset the session variables // Note that session_unset() only works if a session is active. $_SESSION = []; // Check whether a session is active before destroying it in order to avoid a // "Trying to destroy uninitialized session" warning. if (session_status() === PHP_SESSION_ACTIVE) { session_destroy(); } // Problems have been reported on Windows IIS with session data not being // written out without a call to session_write_close(). [Is this necessary // after session_destroy() ??] session_write_close(); } protected function regenerate() : void { // Regenerate the session id session_regenerate_id(true); // Change the lifetime of the key cookie to match the new expiry - see the // comment in destroy(). SessionHandlerDb::regenerateKeyCookie(); } public function get(string $name) { return $_SESSION[$name] ?? null; } public function isset(string $name) : bool { return isset($_SESSION[$name]); } public function set(string $name, $value) : void { $_SESSION[$name] = $value; } public function unset(string $name) : void { unset($_SESSION[$name]); } // Returns the currently logged-in user // This method provides the fallback user for un-logged in users. // Subclasses are expected to override this method, calling it as the parent // if they cannot find a current user. public function getCurrentUser() : ?User { global $auth; if (empty($auth['allow_anonymous_booking'])) { return null; } // Use an empty string for anonymous bookings return new User(''); } // Allows this to be extended with strategies for getting the referer when // HTTP_REFERER is going to be unreliable, eg when the Referrer-Policy is // set to strict-origin. public function getReferrer() : ?string { global $server; return $server['HTTP_REFERER'] ?? null; } // Updates the current and previous pages public function updatePage(string $url) : void { } }