mirror of
				https://github.com/yiisoft/yii2.git
				synced 2025-11-04 22:57:40 +08:00 
			
		
		
		
	Option Security::passwordHashStrategy added
				
					
				
			This commit is contained in:
		@ -58,10 +58,18 @@ class Security extends Component
 | 
			
		||||
    /**
 | 
			
		||||
     * @var string strategy, which should be used to derive a key for encryption.
 | 
			
		||||
     * Available strategies:
 | 
			
		||||
     * - 'pbkdf2' - PBKDF2 key derivation, this option is recommended, but it requires PHP version >= 5.5.0
 | 
			
		||||
     * - 'pbkdf2' - PBKDF2 key derivation. This option is recommended, but it requires PHP version >= 5.5.0
 | 
			
		||||
     * - 'hmac' - HMAC hash key derivation.
 | 
			
		||||
     */
 | 
			
		||||
    public $deriveKeyStrategy = 'hmac';
 | 
			
		||||
    /**
 | 
			
		||||
     * @var string strategy, which should be used to generate password hash.
 | 
			
		||||
     * Available strategies:
 | 
			
		||||
     * - 'password_hash' - use of PHP `password_hash()` function with PASSWORD_DEFAULT algorithm. This option is recommended,
 | 
			
		||||
     *   but it requires PHP version >= 5.5.0
 | 
			
		||||
     * - 'crypt' - use PHP `crypt()` function.
 | 
			
		||||
     */
 | 
			
		||||
    public $passwordHashStrategy = 'crypt';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Encrypts data.
 | 
			
		||||
@ -154,7 +162,7 @@ class Security extends Component
 | 
			
		||||
            case 'hmac':
 | 
			
		||||
                return $this->deriveKeyHmac($password, $salt);
 | 
			
		||||
            default:
 | 
			
		||||
                throw new InvalidConfigException("Unknown Derive key strategy '{$this->deriveKeyStrategy}'");
 | 
			
		||||
                throw new InvalidConfigException("Unknown derive key strategy '{$this->deriveKeyStrategy}'");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -335,14 +343,23 @@ class Security extends Component
 | 
			
		||||
     */
 | 
			
		||||
    public function generatePasswordHash($password, $cost = 13)
 | 
			
		||||
    {
 | 
			
		||||
        switch ($this->passwordHashStrategy) {
 | 
			
		||||
            case 'password_hash':
 | 
			
		||||
                if (!function_exists('password_hash')) {
 | 
			
		||||
                    throw new InvalidConfigException('Password hash key strategy "password_hash" requires PHP >= 5.5.0, either upgrade your environment or use another strategy.');
 | 
			
		||||
                }
 | 
			
		||||
                return password_hash($password, PASSWORD_DEFAULT, ['cost' => $cost]);
 | 
			
		||||
            case 'crypt':
 | 
			
		||||
                $salt = $this->generateSalt($cost);
 | 
			
		||||
                $hash = crypt($password, $salt);
 | 
			
		||||
 | 
			
		||||
                if (!is_string($hash) || strlen($hash) < 32) {
 | 
			
		||||
                    throw new Exception('Unknown error occurred while generating hash.');
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return $hash;
 | 
			
		||||
            default:
 | 
			
		||||
                throw new InvalidConfigException("Unknown password hash strategy '{$this->passwordHashStrategy}'");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -351,6 +368,7 @@ class Security extends Component
 | 
			
		||||
     * @param string $hash The hash to verify the password against.
 | 
			
		||||
     * @return boolean whether the password is correct.
 | 
			
		||||
     * @throws InvalidParamException on bad password or hash parameters or if crypt() with Blowfish hash is not available.
 | 
			
		||||
     * @throws InvalidConfigException on unsupported password hash strategy is configured.
 | 
			
		||||
     * @see generatePasswordHash()
 | 
			
		||||
     */
 | 
			
		||||
    public function validatePassword($password, $hash)
 | 
			
		||||
@ -363,13 +381,22 @@ class Security extends Component
 | 
			
		||||
            throw new InvalidParamException('Hash is invalid.');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        switch ($this->passwordHashStrategy) {
 | 
			
		||||
            case 'password_hash':
 | 
			
		||||
                if (!function_exists('password_verify')) {
 | 
			
		||||
                    throw new InvalidConfigException('Password hash key strategy "password_hash" requires PHP >= 5.5.0, either upgrade your environment or use another strategy.');
 | 
			
		||||
                }
 | 
			
		||||
                return password_verify($password, $hash);
 | 
			
		||||
            case 'crypt':
 | 
			
		||||
                $test = crypt($password, $hash);
 | 
			
		||||
                $n = strlen($test);
 | 
			
		||||
                if ($n < 32 || $n !== strlen($hash)) {
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return $this->compareString($test, $hash);
 | 
			
		||||
            default:
 | 
			
		||||
                throw new InvalidConfigException("Unknown password hash strategy '{$this->passwordHashStrategy}'");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 | 
			
		||||
@ -29,14 +29,6 @@ class SecurityTest extends TestCase
 | 
			
		||||
 | 
			
		||||
    // Tests :
 | 
			
		||||
 | 
			
		||||
    public function testPasswordHash()
 | 
			
		||||
    {
 | 
			
		||||
        $password = 'secret';
 | 
			
		||||
        $hash = $this->security->generatePasswordHash($password);
 | 
			
		||||
        $this->assertTrue($this->security->validatePassword($password, $hash));
 | 
			
		||||
        $this->assertFalse($this->security->validatePassword('test', $hash));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testHashData()
 | 
			
		||||
    {
 | 
			
		||||
        $data = 'known data';
 | 
			
		||||
@ -48,6 +40,40 @@ class SecurityTest extends TestCase
 | 
			
		||||
        $this->assertFalse($this->security->validateData($hashedData, $key));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function dataProviderPasswordHash()
 | 
			
		||||
    {
 | 
			
		||||
        return [
 | 
			
		||||
            [
 | 
			
		||||
                'crypt',
 | 
			
		||||
                false
 | 
			
		||||
            ],
 | 
			
		||||
            [
 | 
			
		||||
                'password_hash',
 | 
			
		||||
                !function_exists('password_hash')
 | 
			
		||||
            ],
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @dataProvider dataProviderPasswordHash
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $passwordHashStrategy
 | 
			
		||||
     * @param boolean $isSkipped
 | 
			
		||||
     */
 | 
			
		||||
    public function testPasswordHash($passwordHashStrategy, $isSkipped)
 | 
			
		||||
    {
 | 
			
		||||
        if ($isSkipped) {
 | 
			
		||||
            $this->markTestSkipped("Unable to test '{$passwordHashStrategy}' password hash strategy");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        $this->security->passwordHashStrategy = $passwordHashStrategy;
 | 
			
		||||
 | 
			
		||||
        $password = 'secret';
 | 
			
		||||
        $hash = $this->security->generatePasswordHash($password);
 | 
			
		||||
        $this->assertTrue($this->security->validatePassword($password, $hash));
 | 
			
		||||
        $this->assertFalse($this->security->validatePassword('test', $hash));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Data provider for [[testEncrypt()]]
 | 
			
		||||
     * @return array test data
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user