Question

I'm building a web app using CakePHP. I want to use bcrypt/Blowfish for password encryption. User registration is working fine including hashing the password using bcrypt. But somehow i cannot login afterwards - the app says that username/password is wrong but the input is correct.

Here's my code regarding authorization:

Auth component setup in AppController:

public $components = array(
    'Session',
    'Auth' => array(
        'loginAction' => array(
            'controller' => 'users',
            'action' => 'login'
        ),
        'authenticate' => array(
            'Form' => array(
                'userModel' => 'User',
                'passwordHasher' => 'Blowfish'
            )
        )
    )
);

Model code:

public $validate = array(
    'username' => array(
        'alphaNumeric' => array(
            'rule'      => 'alphaNumeric',
            'required'  => true,
            'message'   => 'Alphanumeric characters only'
        )
    ),
    'email'=> 'email',
    'password' => array(
        'lengthrule' => array(
            'rule'      => array('minLength', '8'),
            'message'   => 'Minimum 8 characters'
        )
    )
);

public function beforeSave($options = array()) {
    parent::beforeSave();
    $passwordHasher = new BlowfishPasswordHasher();
    $this->data['User']['password'] = $passwordHasher->hash(
        $this->data['User']['password']
    );
    if(empty($this->data[$this->alias]['token'])) {
        $this->data[$this->alias]['token'] = md5(
            $this->data[$this->alias]['username'] .
            $this->data[$this->alias]['email'] .
            $this->data[$this->alias]['created']
        );
    }

    return $this->data;
}

The login action in my controller:

public function login() {
    if ($this->request->is('post')) {

        if ($this->Auth->login()) {
            return $this->redirect(array('controller' => 'users', 'action' => 'edit'));
        }
        $this->Session->setFlash(__('Invalid username or password, try again'));
    }
}

And finally the element, I'm trying to login from:

<div class="users form">
<?php echo $this->Session->flash('auth'); ?>
<?php echo $this->Form->create('User', array('url' => '/users/login')); ?>
    <fieldset>
        <legend>
            <?php echo __('Login :)'); ?>
        </legend>
        <?php echo $this->Form->input('username');
        echo $this->Form->input('password');
    ?>
    </fieldset>
<?php echo $this->Form->end(__('Login')); ?>
<small>no account yet?</small><br />
<?php echo $this->Html->link(__('Register'), '/users/register'); ?>
</div>

update

I found that the hashed string alters with every login attempt - how can this be solved? Might the salt be random? Unfortunately I did not find anything related in the docs

update2

Regarding this post, the altering hash might be correct in this context. But I assume, CakePHP manages using the right salt, itself while trying to login, right?

update3

With editing the BlowfishPasswordHasher class as follows:

public function check($password, $hashedPassword) {
    var_dump(Security::hash($password, 'blowfish', $hashedPassword));
    return $hashedPassword === Security::hash($password, 'blowfish', $hashedPassword);
}

I now made sure that the Auch componenent is using the same hash for every check. But still - the hash, stored in my database is different from the hash, generated by the hasher's check method. Any ideas?

Was it helpful?

Solution

I've found my mistake.

Besides my user registration I implemented E-Mail verification. On verification the user record is being modified and the corresponding call to User->save() caused the password to be hashed within beforeSave() again.

So what worked for me is using my beforeSave() code from above but only hashing the password field again if the token field is empty, which, in my case, can only occur whilst registration, not at or after verification.

OTHER TIPS

Try to use a custom hasher that hashes your passwords with blowfish using php-methods. See the php documentation about the new methods here:

1.Hash passwords on beforeSave() in your model:

public function beforeSave()
{
    $this->data['User']['password'] = password_hash(
        $this->data['User']['password'],
        PASSWORD_BCRYPT
    );

    return true;
}

2.Define Custom Hasher in Auth component of your AppController:

public $components = array(
    'Session',
    'Auth' => array(
        'loginAction' => array(
            'controller' => 'users',
            'action' => 'login'
        ),
        'authenticate' => array(
            'Form' => array(
                'userModel' => 'User',
                'passwordHasher' => 'Custom'
            )
        )
    )
);

3.create custom hasher in app/Controller/Component/Auth/CustomPasswordHasher.php:

App::uses('AbstractPasswordHasher', 'Controller/Component/Auth');
class CustomPasswordHasher extends AbstractPasswordHasher
{
    public function hash($password)
    {
        return $password;
    }

    public function check($password, $hashedPassword)
    {
        return password_verify($password, $hashedPassword);
    }
}

Thats all. Works for me.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top