yii-user salt for every user

Hello,

this is my first post, so dont be very angry if I’ll make some stupid mistakes :)

I recentry installer user extension (+rights, but thats not the topic), and everything looked great, except security - password is only hashed using md5 or sha1, by default. I wanted salts for every user password.

So thats what I did: (maybe this will be helpful for someone)

  1. I created additional column in table "tbl_users", called "salt".

  2. edited my config.php like this:




'params'=>array(

...

'hash_salt_length'	=> '7',

'hash_site_key'	=>	'dlfkgknbcvjkbsdkjflsdkhfdf34534jkHL$@#K$^kb',

...



  1. edited UserModule.php file:



	public static function encrypting($string="",$salt="") {


		$sl = Yii::app()->getParams()->hash_salt_length;

		$site_key = Yii::app()->getParams()->hash_site_key;

		//hashing plain password with added salt

		return hash_hmac('sha256', $string . $salt, $site_key);


		/*

		$hash = Yii::app()->getModule('user')->hash;

		if ($hash=="md5")

			return md5($string);

		if ($hash=="sha1")

			return sha1($string);

		else

			return hash($hash,$string);

		 * 

		 */

	}


	/**

	 * Generates random string, length from config - hash_salt_length

	 * @return <String>

	 */

	public static function randomKey(){

		$salt = "";

		$index = 0;

		$sl = Yii::app()->getParams()->hash_salt_length;


		$letters = array('A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','R','S','T','U','V','Z','0','1','2','3','4','5','6','7','8','9');

		for($i=0;$i<$sl;$i++){

			$index = mt_rand(0, count($letters)-1);

			$salt.= ($index % 2)==0 ? $letters[$index] : strtolower($letters[$index]);


		}

		return $salt;

	}



  1. Searched with my IDE (i like netbeans :) ) for string “encrypting(”, and changed every line, which had not activation key, but password:

that means, files:

AdminController.php, (Around 80 line and 116 line):


$model->salt=UserModule::randomKey();

$model->password=Yii::app()->controller->module->encrypting($model->password,  $model->salt);

ProfileController.php (~78 line):


$new_password->salt = UserModule::randomKey();

						$new_password->password = UserModule::encrypting($model->password, $salt);

RecoveryController.php (~24 line):


$find->salt = UserModule::randomKey();

$find->password = Yii::app()->controller->module->encrypting($form2->password, $find->salt);

RegistrationController.php (~58 line):


$model->salt = UserModule::randomKey();

						$model->password=UserModule::encrypting($model->password,$model->salt);

						$model->verifyPassword=UserModule::encrypting($model->verifyPassword, $model->salt);



UserIdentity.php (~35 line):




else if(Yii::app()->getModule('user')->encrypting($this->password, $user->salt)!==$user->password)



  1. Edit models/User.php "scopes" and "defaultScopes" functions, to add "salt" to selective columns.

I haven’t tested every function yet (such as recovery), but at least i can login :)

And by the way, dont forget to edit users in database. To get what info should be used, put for example to RegistrationController such lines:




echo "salt: ".$salt;

echo "<br />";

echo Yii::app()->controller->module->encrypting("admin",$salt);



Waiting for better solutions :)

Here’s a method for Bcrypt which is better for password & activation key storage:

First, setup the Phpass wrapper extension. There are two wrappers, but I found this one http://www.yiiframework.com/extension/phpass/ to be easier to use.

Just set that up via the provided documentation.

Second, install Yii-User extension!

Third, now we can edit the Yii-User files.

In components/UserIdentity.php on line 35 change:


else if(Yii::app()->getModule('user')->encrypting($this->password)!==$user->password)

TO


else if(!Yii::app()->hasher->checkPassword($this->password, $user->password))

In Controllers/ActivationController.php on line 19 change:


$find->activkey = UserModule::encrypting(microtime());



TO


$find->activkey = Yii::app()->hasher->hashPassword(microtime());

In Controllers/AdminController.php on line 74 change:


$model->activkey=Yii::app()->controller->module->encrypting(microtime().$model->password);

TO


$model->activkey=Yii::app()->hasher->hashPassword(microtime().$model->password);

Now go to line 80 and change:


$model->password=Yii::app()->controller->module->encrypting($model->password);

TO


$model->password=Yii::app()->hasher->hashPassword($model->password);

Now go to line 111 & 112 and change:


$model->password=Yii::app()->controller->module->encrypting($model->password);

$model->activkey=Yii::app()->controller->module->encrypting(microtime().$model->password);

TO


$model->password=Yii::app()->hasher->hashPassword($model->password);

$model->activkey=Yii::app()->hasher->hashPassword(microtime().$model->password);    

In Controllers/ProfileController.php on line 75 & 82 change:


if($model->validate()) {

    $new_password = User::model()->notsafe()->findbyPk(Yii::app()->user->id);

    $new_password->password = UserModule::encrypting($model->password);

    $new_password->activkey=UserModule::encrypting(microtime().$model->password);

    $new_password->save();

    Yii::app()->user->setFlash('profileMessage',UserModule::t("New password is saved."));

    $this->redirect(array("profile"));

                        }

TO


//validate user input

if($model->validate()) {

//generate new hash per new password

$new_hash = Yii::app()->hasher->hashPassword($model->password);

//generate new activation code

$new_actikey = Yii::app()->hasher->hashPassword(microtime().$model->password);

//add into database (Hash)

$command = Yii::app()->db->createCommand();

$command->update('tbl_users', array(

'password'=>$new_hash

), 'id=:id', array(':id'=>Yii::app()->user->id));

//add into database (Activation Key)

$command->update('tbl_users', array(

'activkey'=>$new_actikey

), 'id=:id', array(':id'=>Yii::app()->user->id));

Yii::app()->user->setFlash('profileMessage',UserModule::t("New password is saved."));

$this->redirect(array("profile"));

    }

NOTE: FindbyPk method did not work for me when attempting to save new hash file after password change. Using query builder instead.

In Controllers/RecoveryController.php on line 24 & 25 change:


$find->password = Yii::app()->controller->module->encrypting($form2->password);

$find->activkey=Yii::app()->controller->module->encrypting(microtime().$form2->password);

TO:


$find->password = Yii::app()->hasher->hashPassword($form2->password);

$find->activkey=Yii::app()->hasher->hashPassword(microtime().$form2->password);

In Controllers/RegistrationController.php on line 44 & 47 change:


$soucePassword = $model->password;

$model->activkey=UserModule::encrypting(microtime().$model->password);

$model->password=UserModule::encrypting($model->password);

$model->verifyPassword=UserModule::encrypting($model->verifyPassword);



TO:




if(!empty($model->password) && $model->password==$model->verifyPassword) {

    $soucePassword = $model->password;

    $model->activkey=Yii::app()->hasher->hashPassword(microtime().$model->password);

    //password and verifyPassword

    $model->password=Yii::app()->hasher->hashPassword($model->password);

    $model->verifyPassword=$model->password;

}

That should be it! I have now have a working Yii-User setup that encrypts all my user’s passwords and activation keys to Bcrypt.

Note: I haven’t tested the recovery, but as you can see above it appears to be only two lines of code that needed to be changed. Someone want to confirm this?

It works. Thank you very much for your detailed description. But the last brace in the last code example must be ‘}’ not ‘{’.

You’re welcome.

[color="#006400"]/* Moved to Tips, Snippets, Tutorials from Extensions */[/color]

Thank you! Seems to work well!

In models/UserChangePassword.php verifyOldPassword() I changed this:


if (User::model()->notsafe()->findByPk(Yii::app()->user->id)->password != Yii::app()->getModule('user')->encrypting($this->$attribute))

To this:


if (!Yii::app()->hasher->checkPassword($this->$attribute, User::model()->notsafe()->findByPk(Yii::app()->user->id)->password))

One minor issue I had is that for servers that do not allow url-encoded slashes (%2F) the email activation links wont work. As a kludgey sort of fix I just added:

$model->activkey=str_replace("/","_",Yii::app()->hasher->hashPassword(microtime().$model->password));

To the four locations where the activkey is generated. This seems to have fixed the issue.