Commit 399b6b18 by Qiang Xue

Fixes #4103

parent 83dd3c7d
...@@ -26,11 +26,11 @@ use Yii; ...@@ -26,11 +26,11 @@ use Yii;
* This component provides several configuration parameters, which allow tuning your own balance * This component provides several configuration parameters, which allow tuning your own balance
* between high security and high performance. * between high security and high performance.
* *
* Tip: you may add several `Security` components with different configurations to your application, * > Tip: you may add several `Security` components with different configurations to your application,
* this allows usage of different encryption strategies for different use cases or migrate encrypted data * this allows usage of different encryption strategies for different use cases or migrate encrypted data
* from outdated strategy to the new one. * from outdated strategy to the new one.
* *
* Note: this class requires 'mcrypt' PHP extension available, for the highest security level PHP version >= 5.5.0 required. * > Note: this class requires 'mcrypt' PHP extension. For the highest security level PHP version >= 5.5.0 is recommended.
* *
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @author Tom Worster <fsb@thefsb.org> * @author Tom Worster <fsb@thefsb.org>
...@@ -85,19 +85,11 @@ class Security extends Component ...@@ -85,19 +85,11 @@ class Security extends Component
*/ */
public $useDeriveKeyUniqueSalt = true; public $useDeriveKeyUniqueSalt = true;
/** /**
* @var array list of predefined secret keys in format: keyVerboseName => keyValue * @var string the path or alias of a file that stores the secret keys automatically generated by [[getSecretKey()]].
* While retrieving secret keys [[getSecretKey()]] method usage is recommended. * The file must be writable by Web server process. It contains a JSON hash of key names and key values.
*/ */
public $secretKeys = []; public $secretKeyFile = '@runtime/keys.json';
/**
* @var boolean whether to automatically generate secret key, if it is missing at [[secretKeys]] list
* while being requested via [[getSecretKey()]].
* Usage of this feature is not recommended - it is better to explicitly define list of secret keys.
* However, you may enable this option while project is in development stage to simplify generating of the keys
* list for the future explicit configuration.
* Generated keys can be found under 'runtime' application directory in 'keys.json' file.
*/
public $autoGenerateSecretKey = false;
/** /**
* Encrypts data. * Encrypts data.
...@@ -287,38 +279,35 @@ class Security extends Component ...@@ -287,38 +279,35 @@ class Security extends Component
} }
} }
private $_keys;
/** /**
* Returns a secret key associated with the specified name. * Returns a secret key associated with the specified name.
* If the secret key does not exist and [[autoGenerateSecretKey]] enabled, * If the secret key does not exist, it will be automatically generated and saved in [[secretKeyFile]].
* a random key will be generated and saved in the file "keys.json" under the application's runtime
* directory so that the same secret key can be returned in future requests.
* @param string $name the name that is associated with the secret key * @param string $name the name that is associated with the secret key
* @param integer $length the length of the key that should be generated if not exists * @param integer $length the length of the key that should be generated if not exists
* @throws InvalidParamException if secret key not exist and its generation is not allowed * @param boolean $regenerate whether to regenerate a secret if it already exists
* @return string the secret key associated with the specified name * @return string the secret key associated with the specified name
*/ */
public function getSecretKey($name, $length = 32) public function getSecretKey($name, $length = 32, $regenerate = false)
{ {
if (!array_key_exists($name, $this->secretKeys)) { $keyFile = Yii::getAlias($this->secretKeyFile);
if (!$this->autoGenerateSecretKey) {
throw new InvalidParamException("Unknown secret key '{$name}'"); if ($this->_keys === null) {
} $this->_keys = is_file($keyFile) ? json_decode(file_get_contents($keyFile), true) : [];
$keyFile = Yii::$app->getRuntimePath() . '/keys.json';
if (is_file($keyFile)) {
$keys = json_decode(file_get_contents($keyFile), true);
$this->secretKeys = array_merge($keys, $this->secretKeys);
}
if (!isset($this->secretKeys[$name])) {
$this->secretKeys[$name] = $this->generateRandomKey($length);
file_put_contents($keyFile, json_encode($this->secretKeys));
} }
if (!isset($this->_keys[$name]) || $regenerate) {
$this->_keys[$name] = utf8_encode(static::generateRandomKey($length));
file_put_contents($keyFile, json_encode($this->_keys));
} }
return $this->secretKeys[$name];
return utf8_decode($this->_keys[$name]);
} }
/** /**
* Generates a random key. The key may contain uppercase and lowercase latin letters, digits, underscore, dash and dot. * Generates a random key.
* Note: for delivering high security key, this method requires PHP 'mcrypt' extension. * Note the generated key is a binary string with the specified number of bytes in it.
* @param integer $length the length of the key that should be generated * @param integer $length the length of the key that should be generated
* @return string the generated random key * @return string the generated random key
*/ */
...@@ -453,7 +442,7 @@ class Security extends Component ...@@ -453,7 +442,7 @@ class Security extends Component
*/ */
protected function generateSalt($cost = 13) protected function generateSalt($cost = 13)
{ {
$cost = (int) $cost; $cost = (int)$cost;
if ($cost < 4 || $cost > 31) { if ($cost < 4 || $cost > 31) {
throw new InvalidParamException('Cost must be between 4 and 31.'); throw new InvalidParamException('Cost must be between 4 and 31.');
} }
......
...@@ -1217,7 +1217,7 @@ class Request extends \yii\base\Request ...@@ -1217,7 +1217,7 @@ class Request extends \yii\base\Request
public function getCookieValidationKey() public function getCookieValidationKey()
{ {
if ($this->_cookieValidationKey === null) { if ($this->_cookieValidationKey === null) {
$this->_cookieValidationKey = Yii::$app->getSecurity()->getSecretKey(__CLASS__ . '/' . Yii::$app->id); $this->_cookieValidationKey = Yii::$app->getSecurity()->getSecretKey('cookie.validation.key');
} }
return $this->_cookieValidationKey; return $this->_cookieValidationKey;
......
...@@ -131,25 +131,4 @@ class SecurityTest extends TestCase ...@@ -131,25 +131,4 @@ class SecurityTest extends TestCase
$decryptedData = $this->security->decrypt($encryptedData, $key); $decryptedData = $this->security->decrypt($encryptedData, $key);
$this->assertEquals($data, $decryptedData); $this->assertEquals($data, $decryptedData);
} }
public function testGetSecretKey()
{
$this->security->autoGenerateSecretKey = false;
$keyName = 'testGet';
$keyValue = 'testGetValue';
$this->security->secretKeys = [
$keyName => $keyValue
];
$this->assertEquals($keyValue, $this->security->getSecretKey($keyName));
$this->setExpectedException('yii\base\InvalidParamException');
$this->security->getSecretKey('notExistingKey');
}
/*public function testGenerateSecretKey()
{
$this->security->autoGenerateSecretKey = true;
$keyValue = $this->security->getSecretKey('test');
$this->assertNotEmpty($keyValue);
}*/
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment