From c331a1ae08a98fc6300dce8d407d369db271256f Mon Sep 17 00:00:00 2001 From: =?utf8?q?Gerg=C5=91=20Tisza?= Date: Mon, 30 May 2016 22:05:14 +0000 Subject: [PATCH] Fix encryption code in Session * verify that the algorithm that's about to be used is available * fix exception namespace Bug: T136587 Change-Id: I9f8636bef0e10b4f2b8bfe232a26a8c33376ca04 --- includes/session/Session.php | 139 +++++++++++++++++++++-------------- 1 file changed, 82 insertions(+), 57 deletions(-) diff --git a/includes/session/Session.php b/includes/session/Session.php index 3d48101acc..719f905ee2 100644 --- a/includes/session/Session.php +++ b/includes/session/Session.php @@ -405,6 +405,38 @@ final class Session implements \Countable, \Iterator, \ArrayAccess { ]; } + /** + * Decide what type of encryption to use, based on system capabilities. + * @return array + */ + private function getEncryptionAlgorithm() { + global $wgSessionInsecureSecrets; + + if ( + function_exists( 'openssl_encrypt' ) + && in_array( 'aes-256-ctr', openssl_get_cipher_methods(), true ) + ) { + return [ 'openssl', 'aes-256-ctr' ]; + } elseif ( + function_exists( 'mcrypt_encrypt' ) + && in_array( 'rijndael-128', mcrypt_list_algorithms(), true ) + && in_array( 'ctr', mcrypt_list_modes(), true ) + ) { + return [ 'mcrypt', 'rijndael-128', 'ctr' ]; + } elseif ( $wgSessionInsecureSecrets ) { + // @todo: import a pure-PHP library for AES instead of this + return [ 'insecure' ]; + } else { + throw new \BadMethodCallException( + 'Encryption is not available. You really should install the PHP OpenSSL extension, ' . + 'or failing that the mcrypt extension. But if you really can\'t and you\'re willing ' . + 'to accept insecure storage of sensitive session data, set ' . + '$wgSessionInsecureSecrets = true in LocalSettings.php to make this exception go away.' + ); + } + + } + /** * Set a value in the session, encrypted * @@ -414,8 +446,6 @@ final class Session implements \Countable, \Iterator, \ArrayAccess { * @param mixed $value */ public function setSecret( $key, $value ) { - global $wgSessionInsecureSecrets; - list( $encKey, $hmacKey ) = $this->getSecretKeys(); $serialized = serialize( $value ); @@ -425,27 +455,27 @@ final class Session implements \Countable, \Iterator, \ArrayAccess { // Encrypt // @todo: import a pure-PHP library for AES instead of doing $wgSessionInsecureSecrets $iv = \MWCryptRand::generate( 16, true ); - if ( function_exists( 'openssl_encrypt' ) ) { - $ciphertext = openssl_encrypt( $serialized, 'aes-256-ctr', $encKey, OPENSSL_RAW_DATA, $iv ); - if ( $ciphertext === false ) { - throw new UnexpectedValueException( 'Encryption failed: ' . openssl_error_string() ); - } - } elseif ( function_exists( 'mcrypt_encrypt' ) ) { - $ciphertext = mcrypt_encrypt( 'rijndael-128', $encKey, $serialized, 'ctr', $iv ); - if ( $ciphertext === false ) { - throw new UnexpectedValueException( 'Encryption failed' ); - } - } elseif ( $wgSessionInsecureSecrets ) { - $ex = new \Exception( 'No encryption is available, storing data as plain text' ); - $this->logger->warning( $ex->getMessage(), [ 'exception' => $ex ] ); - $ciphertext = $serialized; - } else { - throw new \BadMethodCallException( - 'Encryption is not available. You really should install the PHP OpenSSL extension, ' . - 'or failing that the mcrypt extension. But if you really can\'t and you\'re willing ' . - 'to accept insecure storage of sensitive session data, set ' . - '$wgSessionInsecureSecrets = true in LocalSettings.php to make this exception go away.' - ); + $algorithm = $this->getEncryptionAlgorithm(); + switch ( $algorithm[0] ) { + case 'openssl': + $ciphertext = openssl_encrypt( $serialized, $algorithm[1], $encKey, OPENSSL_RAW_DATA, $iv ); + if ( $ciphertext === false ) { + throw new \UnexpectedValueException( 'Encryption failed: ' . openssl_error_string() ); + } + break; + case 'mcrypt': + $ciphertext = mcrypt_encrypt( $algorithm[1], $encKey, $serialized, $algorithm[2], $iv ); + if ( $ciphertext === false ) { + throw new \UnexpectedValueException( 'Encryption failed' ); + } + break; + case 'insecure': + $ex = new \Exception( 'No encryption is available, storing data as plain text' ); + $this->logger->warning( $ex->getMessage(), [ 'exception' => $ex ] ); + $ciphertext = $serialized; + break; + default: + throw new \LogicException( 'invalid algorithm' ); } // Seal @@ -464,8 +494,6 @@ final class Session implements \Countable, \Iterator, \ArrayAccess { * @return mixed */ public function getSecret( $key, $default = null ) { - global $wgSessionInsecureSecrets; - // Fetch $encrypted = $this->get( $key, null ); if ( $encrypted === null ) { @@ -493,38 +521,35 @@ final class Session implements \Countable, \Iterator, \ArrayAccess { } // Decrypt - // @todo: import a pure-PHP library for AES instead of doing $wgSessionInsecureSecrets - if ( function_exists( 'openssl_decrypt' ) ) { - $serialized = openssl_decrypt( - base64_decode( $ciphertext ), 'aes-256-ctr', $encKey, OPENSSL_RAW_DATA, base64_decode( $iv ) - ); - if ( $serialized === false ) { - $ex = new \Exception( 'Decyption failed: ' . openssl_error_string() ); - $this->logger->debug( $ex->getMessage(), [ 'exception' => $ex ] ); - return $default; - } - } elseif ( function_exists( 'mcrypt_decrypt' ) ) { - $serialized = mcrypt_decrypt( - 'rijndael-128', $encKey, base64_decode( $ciphertext ), 'ctr', base64_decode( $iv ) - ); - if ( $serialized === false ) { - $ex = new \Exception( 'Decyption failed' ); - $this->logger->debug( $ex->getMessage(), [ 'exception' => $ex ] ); - return $default; - } - } elseif ( $wgSessionInsecureSecrets ) { - $ex = new \Exception( - 'No encryption is available, retrieving data that was stored as plain text' - ); - $this->logger->warning( $ex->getMessage(), [ 'exception' => $ex ] ); - $serialized = base64_decode( $ciphertext ); - } else { - throw new \BadMethodCallException( - 'Encryption is not available. You really should install the PHP OpenSSL extension, ' . - 'or failing that the mcrypt extension. But if you really can\'t and you\'re willing ' . - 'to accept insecure storage of sensitive session data, set ' . - '$wgSessionInsecureSecrets = true in LocalSettings.php to make this exception go away.' - ); + $algorithm = $this->getEncryptionAlgorithm(); + switch ( $algorithm[0] ) { + case 'openssl': + $serialized = openssl_decrypt( base64_decode( $ciphertext ), $algorithm[1], $encKey, + OPENSSL_RAW_DATA, base64_decode( $iv ) ); + if ( $serialized === false ) { + $ex = new \Exception( 'Decyption failed: ' . openssl_error_string() ); + $this->logger->debug( $ex->getMessage(), [ 'exception' => $ex ] ); + return $default; + } + break; + case 'mcrypt': + $serialized = mcrypt_decrypt( $algorithm[1], $encKey, base64_decode( $ciphertext ), + $algorithm[2], base64_decode( $iv ) ); + if ( $serialized === false ) { + $ex = new \Exception( 'Decyption failed' ); + $this->logger->debug( $ex->getMessage(), [ 'exception' => $ex ] ); + return $default; + } + break; + case 'insecure': + $ex = new \Exception( + 'No encryption is available, retrieving data that was stored as plain text' + ); + $this->logger->warning( $ex->getMessage(), [ 'exception' => $ex ] ); + $serialized = base64_decode( $ciphertext ); + break; + default: + throw new \LogicException( 'invalid algorithm' ); } $value = unserialize( $serialized ); -- 2.20.1