rehash password only when changed

[EDITED: this works in yii 1.0. see my reply below for code that works in yii 1.1.]

i’m new to yii, MVC-style programming and PHP. it’s taken me a while to figure out how to keep hashed passwords in a database and not have them get rehashed every time you update the record. i did eventually make it work, following suggestions by Mike in this post.

i’m posting the details here, in case this is of value to any other beginners, and to see if anyone has any critique or suggestions.

the main changes were in the model code for the database table that contains user information. the database field is ‘password’, and i made a new, non-database model field, named ‘newPassword’:




class Contact extends CActiveRecord

{

	public $newPassword;  // proxy for db field "password", which will only be hashed and updated if this is non-empty


	...


	public function newPasswordValidOrEmpty($attribute, $params) {

		if (empty($this->newPassword))

			return;

		$len = strlen(utf8_decode($this->newPassword));

		if (($len >= <img src='http://www.yiiframework.com/forum/public/style_emoticons/default/cool.gif' class='bbc_emoticon' alt='8)' />  &&  ($len <= 16))

			return;

		$this->addError('password', 'Password must be between 8 and 16 characters.');

	}


	...


	public function rules()

	{

		return array(

			array('newPassword','length','min'=>8,'max'=>16,'on'=>'create'),

			array('newPassword',newPasswordValidOrEmpty,'on'=>'update'),


	...


	public function attributeLabels()

	{

		return array(

			'newPassword' => 'Password',


	...


	public function beforeSave() {

		if (!empty($this->newPassword))

			$this->password = hash('sha256', $this->newPassword);

		return true;

	}



the views/.../_form.php needed to include ‘newPassword’ instead of ‘password’:




<?php echo CHtml::activeLabel($model,'newPassword'); ?>

<?php echo CHtml::activePasswordField($model,'newPassword',array('size'=>28,'maxlength'=>16)); ?>



finally, the value for newPassword wasn’t getting copied from the HTTP POST parameters into my model. i’m not up on the details of “massive assignment” in PHP, but the following line in the controller:

$model->attributes=$_POST[‘Contact’];

did not end up copying newPassword, even though it is an element of $_POST[‘Contact’], so i added 2 lines to the controller to copy it explicitly:




	public function actionCreate()

	{

		$model=new Contact;

		if(isset($_POST['Contact']))

		{

			$model->newPassword = $_POST['Contact']['newPassword'];  // add this line

			$model->attributes=$_POST['Contact'];


	...


	public function actionUpdate()

	{

		$model=$this->loadContact();

		if(isset($_POST['Contact']))

		{

			$model->newPassword = $_POST['Contact']['newPassword'];  // add this line

			$model->attributes=$_POST['Contact'];




perhaps there’s a better way to accomplish that?

anyway, that’s what i have so far. i’d be happy to hear any suggested improvements to any of it.

well, the above worked for me in yii 1.0.11, but not in 1.1.

here’s a version that works in yii 1.1.

first, the model code, which is now simpler:




class Contact extends CActiveRecord

{

	public $newPassword;  // proxy for db field "password", which will only be hashed and updated if this is non-empty


	...


	public function rules()

	{

		return array(

			array('newPassword','length','min'=>8,'max'=>16,'allowEmpty'=>false,'on'=>'insert'),

			array('newPassword','length','min'=>8,'max'=>16,'allowEmpty'=>true,'on'=>'update'),


	...


	public function attributeLabels()

	{

		return array(

			'newPassword' => 'Password',


	...


	public function beforeSave() {

		if (!empty($this->newPassword))

			$this->password = hash('sha256', $this->newPassword);

		return true;

	}




in the 1.1 form, ‘password’ appears in 3 places instead of just 2. replace them all with ‘newPassword’.

the controller changes in 1.1 are the same as for 1.0:




        public function actionCreate()

        {

                $model=new Contact;

                if(isset($_POST['Contact']))

                {

                        $model->newPassword = $_POST['Contact']['newPassword'];  // add this line

                        $model->attributes=$_POST['Contact'];


        ...


        public function actionUpdate()

        {

                $model=$this->loadContact();

                if(isset($_POST['Contact']))

                {

                        $model->newPassword = $_POST['Contact']['newPassword'];  // add this line

                        $model->attributes=$_POST['Contact'];



I have field in DB table field named "passwordHash" and setter & getter for property "password" in my model.

In setter "setPassword": set "passwordHash" by wraping value with my hash function.

In getter "getPassword": simply return "passwordHash".

So I always use "password" property (in forms, etc).

I set attributes as array and the password is auto-hashed too.

Thanks a lot,

this is the easyest but very efficient way of rehash passwords only if changed.

Ciao

A

Hi all. I’m making for the user to change its password, but I have one problem: I don’t want the hashed password to be displayed in the password field.

I have tried to set the password to null in the afterFind model function. It worked, but the login functionality stopped working.

I have found this post. The last comment talks about this issue, but I didn’t understand much the solution he gave.

Anyone has any thoughts regarding this?

thank you