User Authentication

Hi, I’ve been loosely following the tutorial here, but with a MySQL backend, and developing a different application.

I was absolutely fine until I got to the authorisation section. As I’m not using the blog database data, I don’t have users saved down in tbl_user. I can’t work out how to encode a user in the table to make the system work, it just refuses to authorise no matter what. I’ve tried encoding with SHA1 and MD5 directly in MySQL, but I suspect I need to salt the password and save a salt?

Can anyone talk my through the right fields and encoding to make the setup work?

Thanks.

It’s up to you how to store passwords in DB during user creation (or pwd change), just remember to implement the proper algorythm in authenticate().

So if you choose to store plain-text passwords, you should compare $record->password and $this->password in authenticate(), and if you choose to crypt, then you can compare $record->password and your_crypt_function($this->password).

You can use this helper for crypting.

PS. I wonder if this line of code


crypt($this->password,$record->password)

is a typo. Is it ok to use password itself for salting?..

So if we’re just using


CPasswordHelper::verifyPassword($password,$this->password);

…then there are no guidelines on creating early users manually in the database prior to the creation of a registration script? If we were using verifyPassword to check an entry, what would the database entry we were matching against look like? I could create a script to create some users, but if that wasn’t doing the right thing, I wouldn’t know if it was that script or the checker that was failing.

I’m looking for a bit of SQL like, for example:


INSERT INTO tbl_users (username, password) values ('joebloggs', MD5('Password'))

or similar.

From the docs:


$hash = CPasswordHelper::hashPassword($password);

This hash can be stored in a database (e.g. CHAR(64) CHARACTER SET latin1)



As far as I can tell from the docs, that’s an MD5 hash. Is that right?


public static function hashPassword($password,$cost=13)

{

    self::checkBlowfish();

    $salt=self::generateSalt($cost);

    $hash=crypt($password,$salt);


    if(!is_string($hash) || (function_exists('mb_strlen') ? mb_strlen($hash, '8bit') : strlen($hash))<32)

        throw new CException(Yii::t('yii','Internal error while generating hash.'));


    return $hash;

}

did you ever get it to work, I am stuck in the same spot, it won’t even login with the demo/demo logins,

very disappointed as I was hoping to use this feature for my website and am now stuck on it. let me know if you figure it out.

CPasswordHelper::hashPassword() uses Blowfish as the crypt algorithm.

This introduces excellent security on the password but, it also introduces some complexity.

The first is that you can’t directly pre-propulate a MySQL table of users by enclosing the password field with MD5. You must first write a PHP script that hashes the password using the CPasswordHelper::hashPassword() function and insert the hashed value into the database.

The second is that you also can not simply do this

if($model->password == CPasswordHelper::hashPassword($form->password)) {

}

The reason is because CPasswordHelper::hashPassword() generates a different hash for the same input every time that it is called. You must instead use something like

if(CPasswordHelper::verifyPassword($form->password, $model->password)) {

}

The third thing you need to be aware of is the $cost parameter to CPasswordHelper::hashPassword($password, $cost = 13). The default of 13 may be too high for a hosted server.

You can use something like this to find the optimal cost for your server:




// Find an optimal cost to use

$timeTarget = 0.2; 

            

$cost = 5;

            

do {

    $cost++;

    $start = microtime(true);

    CPasswordHelper::hashPassword("test", $cost);

    $end = microtime(true);

} while (($end - $start) < $timeTarget);


echo "Optimal Cost: " . $cost . "\n";



Lastly, the PHP manual for the crypt() function states that in versions of PHP 5.3.7 and later, you should no longer prefix the salt with $2a$ and instead should use $2y$ because of a security flaw. Well, CPasswordHelper’s generateSalt function uses the $2a$ and so, like I did, you should extend CPasswordHelper and fix the generateSalt function by changing the prefix to $2y$ if you are using PHP 5.3.7 or later.

And here is my UserIdentity::authenticate() function. Please note that it calls methods and properties that are specific to my implementation but, I hope it helps you.




    /**

     * Authenticates a user by either username or e-mail address.

     * Searching is case insensitive!

     * Uses CPasswordHelper which uses the Blowfish Crypto Algo.

     * @return boolean whether authentication succeeded.

     */

    public function authenticate()

    {

        $this->findUser();

        

        if($this->_user === null) {

            if($this->_useemail) {

                $this->errorCode = self::ERROR_EMAIL_INVALID;

            } else {

                $this->errorCode = self::ERROR_USERNAME_INVALID;

            }

        } else {

            // We have a valid user account that we need to authenticate!

            if(!$this->_user->verifyPassword($this->password)) {

                $this->errorCode = self::ERROR_PASSWORD_INVALID;

                $this->_user->loginFailed();

            } else {

                $this->processUserStatus();

            }

        }

        return !$this->errorCode;

    } // end authenticate()