src/UltimateSessions/UltimateSessionHandlerTrait.php
<?php namespace MikeBrant\UltimateSessions; use Defuse\Crypto\Crypto;use Defuse\Crypto\Key; /** * Trait UltimateSessionHandlerTrait * * This trait is intended to be composed into any class which implements the * UltimateSessionHandlerInterface and has a dependency on that interface, so * must be used jointly. * * This trait exposes basic functionality for: * * - creating and storing session configuration; * - setting appropriate php.ini session.* settings based on constants in * interface; * - handling encryption/decryption of session data, including encryption key * generation; * - setting and destroying cookie which stores encryption key with the client; * - setting the object using the trait as session handler via * session_set_save_handler(). * * @package MikeBrant\UltimateSessions */trait UltimateSessionHandlerTrait{ /** * Object storing session handler configuration values as derived from an * UltimateSessionHandleConfig object and needed for classes inheriting * UltimateSessionHandlerTrait. * * @var UltimateSessionHandlerConfig Session handler configuration */ protected $config = null; /** * Encryption key object as returned by * Defuse\Crypto\Key::createNewRandomKey() * * @var Key */ protected $encryptionKey = null; /** * Method to initialize session handler. This method must be called in * constructor from any class inheriting UltimateSessionHandlerTrait. * * @return void * @throws \Exception */ public function sessionHandlerInit() { try { $this->configureIniSettings(); } catch (\Exception $e) {Missing class import via use statement (line '59', column '23'). throw new \Exception( "Unable to establish session using Ultimate Sessions library. " . "Please see previous exception in chain for details.", 0, $e ); } /** @noinspection PhpParamsInspection */ $this->setSessionHandler($this); } /** * Method to validate session ID values for use by classes implementing * UltimateSessionHandlerInterface. * * @param $sessionId * @return void * @throws \InvalidArgumentException */ public function validateSessionId($sessionId) { $regex = UltimateSessionHandlerInterface::SESSION_ID_REGEX_PHP_BELOW_7_1_0; if(version_compare(PHP_VERSION, '7.1.0', '>=')) { $regex = UltimateSessionHandlerInterface::SESSION_ID_REGEX_PHP_7_1_0; } if(!preg_match($regex, $sessionId)) {Missing class import via use statement (line '85', column '23'). throw new \InvalidArgumentException('Value did not match session ID regex.'); } } /** * Method to set ASCII encryption key to cookie based on values derived from * configuration. This method both sends header via setcookie() and sets * $_COOKIE. * * @param $sessionId * @param $asciiKey * @return void * @throws \InvalidArgumentException */setEncryptionKeyCookie accesses the super-global variable $_COOKIE. public function setEncryptionKeyCookie($sessionId, $asciiKey) { $this->validateSessionId($sessionId); if(empty($asciiKey) || !is_string($asciiKey)) {Missing class import via use statement (line '103', column '23'). throw new \InvalidArgumentException( 'Value must be non-zero length string' ); } $cookieName = $this->config->keyCookiePrefix . $sessionId; setcookie( $cookieName, $asciiKey, $this->config->cookieLifetime, $this->config->cookiePath, $this->config->cookieDomain, $this->config->cookieSecure, true ); $_COOKIE[$cookieName] = $asciiKey; } /** * Method to delete ASCII encryption key cookie. This method both sends * expired cookie header via setcookie and unsets the key from $_COOKIE. * * @param $sessionId * @return void * @throws \InvalidArgumentException */deleteEncryptionKeyCookie accesses the super-global variable $_COOKIE. public function deleteEncryptionKeyCookie($sessionId) { $this->validateSessionId($sessionId); $cookieName = $this->config->keyCookiePrefix . $sessionId; setcookie( $cookieName, null, -1, $this->config->cookiePath, $this->config->cookieDomain, $this->config->cookieSecure, true ); unset($_COOKIE[$cookieName]); } /** * Method to associate encryption key cookie to a new session id. Thi * * @param $oldSessionId * @param $newSessionId * @throws \Defuse\Crypto\Exception\BadFormatException * @throws \Defuse\Crypto\Exception\EnvironmentIsBrokenException * @throws \InvalidArgumentException */changeKeyCookieSessionId accesses the super-global variable $_COOKIE. public function changeKeyCookieSessionId($oldSessionId, $newSessionId) { $this->validateSessionId($oldSessionId); $this->validateSessionId($newSessionId); $oldCookieName = $this->config->keyCookiePrefix . $oldSessionId; $asciiKey = ''; if(!empty($_COOKIE[$oldCookieName])) { $asciiKey = $_COOKIE[$oldCookieName]; } $this->deleteEncryptionKeyCookie($oldSessionId); $this->setEncryptionKeyCookie( $newSessionId, $asciiKey ); } /** * Method to perform encryption of session data. * * @param string $sessionId * @param string $sessionData * @return string * @throws \Defuse\Crypto\Exception\BadFormatException * @throws \Defuse\Crypto\Exception\EnvironmentIsBrokenException * @throws \InvalidArgumentException */ public function encrypt($sessionId, $sessionData) { $key = $this->getEncryptionKey($sessionId);Avoid using static access to class '\Defuse\Crypto\Crypto' in method 'encrypt'. return Crypto::encrypt($sessionData, $key); } /** * Method that provides guaranteed return of Defuse\Crypto\Key object for * use in encryption. * * @param $sessionId * @return Key * @throws \Defuse\Crypto\Exception\BadFormatException * @throws \Defuse\Crypto\Exception\EnvironmentIsBrokenException * @throws \InvalidArgumentException */ public function getEncryptionKey($sessionId) { if($this->encryptionKey instanceof Key === false) { return $this->setEncryptionKey($sessionId); } return $this->encryptionKey; } /** * Method which instantiates Defuse\Crypto\Key object and sets it on * encrpytionKey property. Key can either be recovered from encryption * key cookie or created from scratch. * * @param $sessionId * @return Key * @throws \Defuse\Crypto\Exception\BadFormatException * @throws \Defuse\Crypto\Exception\EnvironmentIsBrokenException * @throws \InvalidArgumentException */setEncryptionKey accesses the super-global variable $_COOKIE. public function setEncryptionKey($sessionId) { $cookieName = $this->config->keyCookiePrefix . $sessionId; if(!empty($_COOKIE[$cookieName])) {Avoid using static access to class '\Defuse\Crypto\Key' in method 'setEncryptionKey'. $this->encryptionKey = Key::loadFromAsciiSafeString($_COOKIE[$cookieName]); return $this->encryptionKey; }Avoid using static access to class '\Defuse\Crypto\Key' in method 'setEncryptionKey'. $this->encryptionKey = Key::createNewRandomKey(); $this->setEncryptionKeyCookie( $sessionId, $this->encryptionKey->saveToAsciiSafeString() ); return $this->encryptionKey; } /** * Method to perform decryption of session data. * * @param string $sessionId * @param string $sessionData * @return string * @throws \Defuse\Crypto\Exception\BadFormatException * @throws \Defuse\Crypto\Exception\EnvironmentIsBrokenException * @throws \Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException * @throws \InvalidArgumentException */ public function decrypt($sessionId, $sessionData) { $key = $this->getEncryptionKey($sessionId); if(empty($sessionData)) { return $sessionData; }Avoid using static access to class '\Defuse\Crypto\Crypto' in method 'decrypt'. return Crypto::decrypt($sessionData, $key); } /** * Make sure environment has proper php.ini settings for sessions. * Provide appropriate settings for session ID hash (PHP < 7.1) or random ID * generation (PHP 7.1+). * * @return void * @throws \RuntimeException */Function `configureIniSettings` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring. private function configureIniSettings() { /** * Ignore these lines from code coverage as they are covered in build * tests against different PHP versions. * * @codeCoverageIgnoreStart */ $requiredIniSettings = array_merge( UltimateSessionHandlerInterface::REQUIRED_INI_SETTINGS, UltimateSessionHandlerInterface::REQUIRED_INI_SETTINGS_PHP_BELOW_7_1_0 ); if (version_compare(PHP_VERSION, '7.1.0', '>=')) { $requiredIniSettings = array_merge( UltimateSessionHandlerInterface::REQUIRED_INI_SETTINGS, UltimateSessionHandlerInterface::REQUIRED_INI_SETTINGS_PHP_7_1_0 ); } /** @codeCoverageIgnoreEnd */ foreach($requiredIniSettings as $setting => $value) { if (ini_get($setting) === $value) { continue; } if (ini_set($setting, $value) === false) {Missing class import via use statement (line '282', column '27'). throw new \RuntimeException( "Unable to set php.ini setting '{$setting}' to '{$value}'. " . "This is a requirement for secure sessions." ); } } } /** * Method that sets the UltimateSessionHandlerInterface object to * session_set_save_handler(). * * @param UltimateSessionHandlerInterface $handler * @return void */ private function setSessionHandler(UltimateSessionHandlerInterface $handler) { session_set_save_handler($handler, true); }}