In this article I will show you how to implement a secure password hashing mechanism in your Yii projects using a bCrypt class.
The inspiration for this wiki came from this discussion on StackOverflow.
class bCrypt { private $rounds; private $prefix; public function __construct($prefix = '', $rounds = 12) { if(CRYPT_BLOWFISH != 1) { throw new Exception("bcrypt not supported in this installation. See http://php.net/crypt"); } $this->rounds = $rounds; $this->prefix = $prefix; } public function hash($input) { $hash = crypt($input, $this->getSalt()); if(strlen($hash) > 13) return $hash; return false; } public function verify($input, $existingHash) { $hash = crypt($input, $existingHash); return $hash === $existingHash; } private function getSalt() { // the base64 function uses +'s and ending ='s; translate the first, and cut out the latter return sprintf('$2a$%02d$%s', $this->rounds, substr(strtr(base64_encode($this->getBytes()), '+', '.'), 0, 22)); } private function getBytes() { $bytes = ''; if(function_exists('openssl_random_pseudo_bytes') && (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN')) { // OpenSSL slow on Win $bytes = openssl_random_pseudo_bytes(18); } if($bytes === '' && is_readable('/dev/urandom') && ($hRand = @fopen('/dev/urandom', 'rb')) !== FALSE) { $bytes = fread($hRand, 18); fclose($hRand); } if($bytes === '') { $key = uniqid($this->prefix, true); // 12 rounds of HMAC must be reproduced / created verbatim, no known shortcuts. // Salsa20 returns more than enough bytes. for($i = 0; $i < 12; $i++) { $bytes = hash_hmac('sha512', microtime() . $bytes, $key, true); usleep(10); } } return $bytes; } }
'import'=>array( 'application.vendors.*' ),
protected function afterValidate() { $this->password = $this->encrypt($this->password); return parent::afterValidate(); } public function encrypt($value) { $enc = NEW bCrypt(); return $enc->hash($value); }
} else if (!bCrypt::verify($this->password, $user->password)) { $this->errorCode=self::ERROR_PASSWORD_INVALID;
How does this work?
By hooking into the User model's afterValidate() method we can drop in this code without any major changes to the UserIdentity or other components. What happens is that when your login form is submitted the inputs are validated (username, password). If they pass validation, the password value gets sent through the encrypt function and returned as a hash for database comparison.
The only remaining work to do is to ensure that your password field in the database is large enough to hold the values. I recommend using char(60) as field type.
Total 3 comments
I've updated the article, so you could use sha512 instead of salsa20 that was mentioned.
im currently using PHP/5.4.4 and salsa20 algorithm was removed
http://php.net/manual/en/function.hash-algos.php
https://bugs.php.net/bug.php?id=60783
please update this extension
http://www.yiiframework.com/wiki/240/authenticating-against-phpass-hashes-with-yii/
Leave a comment
Please login to leave your comment.