vendredi 11 janvier 2013

Crypt password with salt in Yii framework

Hello,

Today I will share my experience while learning PHP and Yii framework by using the standard PHP function crypt.

First of all, here you can find some basic theory about why do you need use salt to encrypt the passwords.

Let's discuss the parts of your Yii app you should modify in order to benefit the more robust security for password encryption:
  1. You should create (or generate with vii) User model with userName, userPass and userSalt fields. 
  2. While saving the new user in your Database, you should encrypt the user password with randomly generated salt. I do this in User Model class, by overriding the method beforeSave. Like this:
    protected function beforeSave()
        {
            if(parent::beforeSave())
            {
                if($this->isNewRecord)
                {
                    $this->user_salt=   utf8_encode( mcrypt_create_iv(30) );
                    $newPassword =  utf8_encode( crypt($this->user_pass, $this->user_salt) );
                    $this->user_pass = $newPassword;
                }
                return true;
            }
            else
                return false;
        }
    

    Code explanation:
    - use standard PHP 5.4 mcrypt_create_iv function to generate the salt (this function available only since version 5.4);
    - use standard crypt function with 2 parameters - first is the user password and the second one is the salt.
    - I'm using the standard PHP utf8_encode function for properly view the password and salt in the Database. It's not mandatory.
  3. The last step is modifying the existing authentificate method from protected/components/UserIdentity.php to check the credentials against the Database with salted password:
    public function authenticate()
     {
    
            $record=User::model()->findByAttributes(array('user_name'=>$this->username));
            if($record===null)
                $this->errorCode=self::ERROR_USERNAME_INVALID;
            else if($record->user_pass !==  utf8_encode( crypt($this->password,$record->user_pass))){
                $this->errorCode=self::ERROR_PASSWORD_INVALID;
            }
            else
            {
                $this->username=$record->user_name;
                $this->errorCode=self::ERROR_NONE;
            }
    
            
            return !$this->errorCode;
     }
    
    The code is obvious except the line 7 while we are using the function crypt where the second parameter is the encrypted user password(not salt)! Eh, yes, as the documentation of crypt function says, you should always use the encrypted data as a salt. If you will pass the $record->user_salt, you will get the wrong string that will not match your encrypted password. This trick caused me to spend few hours on debugging the issue.

2 commentaires:

Anonyme a dit…

Thank you so much for your explanation.
I have added your code and it works but now I am having another problem. The encrypted version of the password is showing in the Yii admin tables, what would you suggest to stop thie from happening please? Also, How can I test that we can decrypt the hashed password?

Thank you,

Ben

Voronetskyy Yevgen a dit…

Actually, your first issue is just about your particular GUI model, you can easy exclude the password column from your view.
The second question. You must decrypt the crypted data and make the equals operation for string. for decryption, you have to use this function crypt($this->password,$record->user_pass), as described in the article.