Tuesday, June 22, 2010

Saving encrypted passwords for new users

When using PASSWORD() encrypted passwords for users, I need to save the encrypted version when the file is created or when the password is changed. However, I don't want it to run the encryption every time the file is saved if it's pulled in the encrypted value already.

Putting the job of encrypting the password on the User model, I've added an encryptPassword() function to the model, which can be called by the controller after it receives the post data as such:

public function encryptPassword()
{
    $p = $this->userpass;
    $this->userpass = new CDbExpression('PASSWORD(:up)', array(':up'=>$p));
}

And modify the actionCreate as such:
...
$model->attributes = $_POST['User']; // existing line
if ( $model->validate()){    
   $model->encryptPassword();
   if ( $model->save( false )){   // modify existing line to pass in false param
      // .. existing code here
   }
} 
- OR -
To automate the process, alter the User model further by adding:
protected function beforeSave()
{
  if ( $this->isNewRecord )
     $this->encryptPassword();
  return parent::beforeSave();
}

Then, if you have a change password form, you can handle it in the same fashion.

3 comments:

  1. Every time I take some simple functionality from the controller and convert it in to model logic it makes the code that much more scalable.

    Nice write up, I didn't know there was a PASSWORD MySQL function.

    ReplyDelete
  2. I found this topic very interesting..
    I was able to use it on my app but i dont know how to decrypt the values upon display..

    help me please..

    Best Regards..

    ReplyDelete
  3. If your using MySQL encryption to save the passwords in the database, you cannot decrypt them. If someone has forgotten their password you can only reset it, not tell them what it was.

    If you want to be able to decrypt what you're storing, you should consider using the Yii::app()->securityManager (http://www.yiiframework.com/doc/api/1.1/CSecurityManager) instead of MySQL level encryption.

    You just change your encryptPassword function to call the security manager's encrypt function, and create a decryptPassword function that calls the decrypt. To work properly over time, you'll need to set the encryption key for the security manager in your main config so that it's consistent. To do so, your password field in the database will need to be of type BLOB.

    ReplyDelete