26use Psr\Log\LoggerInterface;
48class Session implements \Countable, \Iterator, \ArrayAccess {
73 $this->backend->deregisterSession( $this->index );
81 return $this->backend->getId();
90 return $this->backend->getSessionId();
98 return $this->backend->resetId();
106 return $this->backend->getProvider();
117 return $this->backend->isPersistent();
127 $this->backend->persist();
139 $this->backend->unpersist();
148 return $this->backend->shouldRememberUser();
157 $this->backend->setRememberUser( $remember );
165 return $this->backend->getRequest( $this->index );
173 return $this->backend->getUser();
181 return $this->backend->getAllowedUserRights();
189 return $this->backend->canSetUser();
200 $this->backend->setUser( $user );
208 return $this->backend->suggestLoginUsername( $this->index );
219 return $this->backend->shouldForceHTTPS();
230 $this->backend->setForceHTTPS( $force );
238 return $this->backend->getLoggedOutTimestamp();
246 $this->backend->setLoggedOutTimestamp( $ts );
255 return $this->backend->getProviderMetadata();
262 $data = &$this->backend->getData();
265 $this->backend->dirty();
267 if ( $this->backend->canSetUser() ) {
268 $this->backend->setUser(
new User );
270 $this->backend->save();
280 $this->backend->renew();
293 $request->
setSessionId( $this->backend->getSessionId() );
294 return $this->backend->getSession( $request );
303 public function get( $key, $default = null ) {
304 $data = &$this->backend->getData();
305 return array_key_exists( $key, $data ) ? $data[$key] : $default;
315 $data = &$this->backend->getData();
316 return array_key_exists( $key, $data );
324 public function set( $key, $value ) {
325 $data = &$this->backend->getData();
326 if ( !array_key_exists( $key, $data ) || $data[$key] !== $value ) {
327 $data[$key] = $value;
328 $this->backend->dirty();
336 public function remove( $key ) {
337 $data = &$this->backend->getData();
338 if ( array_key_exists( $key, $data ) ) {
339 unset( $data[$key] );
340 $this->backend->dirty();
354 public function getToken( $salt =
'', $key =
'default' ) {
356 $secrets = $this->
get(
'wsTokenSecrets' );
357 if ( !is_array( $secrets ) ) {
360 if ( isset( $secrets[$key] ) && is_string( $secrets[$key] ) ) {
361 $secret = $secrets[$key];
363 $secret = \MWCryptRand::generateHex( 32 );
364 $secrets[$key] = $secret;
365 $this->
set(
'wsTokenSecrets', $secrets );
368 if ( is_array( $salt ) ) {
369 $salt = implode(
'|', $salt );
371 return new Token( $secret, (
string)$salt, $new );
382 $secrets = $this->
get(
'wsTokenSecrets' );
383 if ( is_array( $secrets ) && isset( $secrets[$key] ) ) {
384 unset( $secrets[$key] );
385 $this->
set(
'wsTokenSecrets', $secrets );
393 $this->
remove(
'wsTokenSecrets' );
404 $userSecret = $this->
get(
'wsSessionSecret', null );
405 if ( $userSecret ===
null ) {
406 $userSecret = \MWCryptRand::generateHex( 32 );
407 $this->
set(
'wsSessionSecret', $userSecret );
409 $iterations = $this->
get(
'wsSessionPbkdf2Iterations', null );
410 if ( $iterations ===
null ) {
412 $this->
set(
'wsSessionPbkdf2Iterations', $iterations );
415 $keymats = hash_pbkdf2(
'sha256', $wikiSecret, $userSecret, $iterations, 64,
true );
417 substr( $keymats, 0, 32 ),
418 substr( $keymats, 32, 32 ),
429 if ( self::$encryptionAlgorithm ===
null ) {
430 if ( function_exists(
'openssl_encrypt' ) ) {
431 $methods = openssl_get_cipher_methods();
432 if ( in_array(
'aes-256-ctr', $methods,
true ) ) {
433 self::$encryptionAlgorithm = [
'openssl',
'aes-256-ctr' ];
436 if ( in_array(
'aes-256-cbc', $methods,
true ) ) {
437 self::$encryptionAlgorithm = [
'openssl',
'aes-256-cbc' ];
444 self::$encryptionAlgorithm = [
'insecure' ];
448 throw new \BadMethodCallException(
449 'Encryption is not available. You really should install the PHP OpenSSL extension. ' .
450 'But if you really can\'t and you\'re willing ' .
451 'to accept insecure storage of sensitive session data, set ' .
452 '$wgSessionInsecureSecrets = true in LocalSettings.php to make this exception go away.'
476 $iv = random_bytes( 16 );
478 switch ( $algorithm[0] ) {
480 $ciphertext = openssl_encrypt(
$serialized, $algorithm[1], $encKey, OPENSSL_RAW_DATA, $iv );
481 if ( $ciphertext ===
false ) {
482 throw new \UnexpectedValueException(
'Encryption failed: ' . openssl_error_string() );
486 $ex = new \Exception(
'No encryption is available, storing data as plain text' );
487 $this->logger->warning( $ex->getMessage(), [
'exception' => $ex ] );
491 throw new \LogicException(
'invalid algorithm' );
495 $sealed = base64_encode( $iv ) .
'.' . base64_encode( $ciphertext );
496 $hmac = hash_hmac(
'sha256', $sealed, $hmacKey,
true );
497 $encrypted = base64_encode( $hmac ) .
'.' . $sealed;
500 $this->
set( $key, $encrypted );
511 $encrypted = $this->
get( $key, null );
512 if ( $encrypted ===
null ) {
521 $pieces = explode(
'.', $encrypted, 4 );
522 if (
count( $pieces ) !== 3 ) {
523 $ex = new \Exception(
'Invalid sealed-secret format' );
524 $this->logger->warning( $ex->getMessage(), [
'exception' => $ex ] );
527 list( $hmac, $iv, $ciphertext ) = $pieces;
529 $integCalc = hash_hmac(
'sha256', $iv .
'.' . $ciphertext, $hmacKey,
true );
530 if ( !hash_equals( $integCalc, base64_decode( $hmac ) ) ) {
531 $ex = new \Exception(
'Sealed secret has been tampered with, aborting.' );
532 $this->logger->warning( $ex->getMessage(), [
'exception' => $ex ] );
538 switch ( $algorithm[0] ) {
540 $serialized = openssl_decrypt( base64_decode( $ciphertext ), $algorithm[1], $encKey,
541 OPENSSL_RAW_DATA, base64_decode( $iv ) );
543 $ex = new \Exception(
'Decyption failed: ' . openssl_error_string() );
544 $this->logger->debug( $ex->getMessage(), [
'exception' => $ex ] );
549 $ex = new \Exception(
550 'No encryption is available, retrieving data that was stored as plain text'
552 $this->logger->warning( $ex->getMessage(), [
'exception' => $ex ] );
556 throw new \LogicException(
'invalid algorithm' );
574 return $this->backend->delaySave();
584 $this->backend->save();
594 $data = &$this->backend->getData();
595 return count( $data );
599 #[\ReturnTypeWillChange]
601 $data = &$this->backend->getData();
602 return current( $data );
606 #[\ReturnTypeWillChange]
608 $data = &$this->backend->getData();
614 $data = &$this->backend->getData();
620 $data = &$this->backend->getData();
626 $data = &$this->backend->getData();
627 return key( $data ) !==
null;
636 $data = &$this->backend->getData();
637 return isset( $data[$offset] );
648 #[\ReturnTypeWillChange]
650 $data = &$this->backend->getData();
651 if ( !array_key_exists( $offset, $data ) ) {
652 $ex = new \Exception(
"Undefined index (auto-adds to session with a null value): $offset" );
653 $this->logger->debug( $ex->getMessage(), [
'exception' => $ex ] );
655 return $data[$offset];
660 $this->set( $offset, $value );
665 $this->remove( $offset );
unserialize( $serialized)
$wgSecretKey
This should always be customised in LocalSettings.php.
$wgSessionPbkdf2Iterations
Number of internal PBKDF2 iterations to use when deriving session secrets.
$wgSessionSecret
Secret for session storage.
$wgSessionInsecureSecrets
If for some reason you can't install the PHP OpenSSL extension, you can set this to true to make Medi...
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
The WebRequest class encapsulates getting at data passed in the URL or via a POSTed form stripping il...
setSessionId(SessionId $sessionId)
Set the session for this request.
foreach( $res as $row) $serialized