Commit 399b6b18 by Qiang Xue

Fixes #4103

parent 83dd3c7d
......@@ -26,11 +26,11 @@ use Yii;
* This component provides several configuration parameters, which allow tuning your own balance
* 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
* 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 Tom Worster <fsb@thefsb.org>
......@@ -85,19 +85,11 @@ class Security extends Component
*/
public $useDeriveKeyUniqueSalt = true;
/**
* @var array list of predefined secret keys in format: keyVerboseName => keyValue
* While retrieving secret keys [[getSecretKey()]] method usage is recommended.
* @var string the path or alias of a file that stores the secret keys automatically generated by [[getSecretKey()]].
* The file must be writable by Web server process. It contains a JSON hash of key names and key values.
*/
public $secretKeys = [];
/**
* @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;
public $secretKeyFile = '@runtime/keys.json';
/**
* Encrypts data.
......@@ -233,7 +225,7 @@ class Security extends Component
protected function deriveKeyHmac($password, $salt)
{
$hmac = hash_hmac($this->derivationHash, $salt . pack('N', 1), $password, true);
$xorsum = $hmac;
$xorsum = $hmac;
for ($i = 1; $i < $this->derivationIterations; $i++) {
$hmac = hash_hmac($this->derivationHash, $hmac, $password, true);
$xorsum ^= $hmac;
......@@ -287,38 +279,35 @@ class Security extends Component
}
}
private $_keys;
/**
* Returns a secret key associated with the specified name.
* If the secret key does not exist and [[autoGenerateSecretKey]] enabled,
* 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.
* If the secret key does not exist, it will be automatically generated and saved in [[secretKeyFile]].
* @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
* @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
*/
public function getSecretKey($name, $length = 32)
public function getSecretKey($name, $length = 32, $regenerate = false)
{
if (!array_key_exists($name, $this->secretKeys)) {
if (!$this->autoGenerateSecretKey) {
throw new InvalidParamException("Unknown secret key '{$name}'");
}
$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));
}
$keyFile = Yii::getAlias($this->secretKeyFile);
if ($this->_keys === null) {
$this->_keys = is_file($keyFile) ? json_decode(file_get_contents($keyFile), true) : [];
}
return $this->secretKeys[$name];
if (!isset($this->_keys[$name]) || $regenerate) {
$this->_keys[$name] = utf8_encode(static::generateRandomKey($length));
file_put_contents($keyFile, json_encode($this->_keys));
}
return utf8_decode($this->_keys[$name]);
}
/**
* Generates a random key. The key may contain uppercase and lowercase latin letters, digits, underscore, dash and dot.
* Note: for delivering high security key, this method requires PHP 'mcrypt' extension.
* Generates a random key.
* 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
* @return string the generated random key
*/
......@@ -453,7 +442,7 @@ class Security extends Component
*/
protected function generateSalt($cost = 13)
{
$cost = (int) $cost;
$cost = (int)$cost;
if ($cost < 4 || $cost > 31) {
throw new InvalidParamException('Cost must be between 4 and 31.');
}
......@@ -489,4 +478,4 @@ class Security extends Component
}
return $diff === 0;
}
}
\ No newline at end of file
}
......@@ -1217,7 +1217,7 @@ class Request extends \yii\base\Request
public function getCookieValidationKey()
{
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;
......
......@@ -131,25 +131,4 @@ class SecurityTest extends TestCase
$decryptedData = $this->security->decrypt($encryptedData, $key);
$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