1

Until now I have stored all passwords in plain text because the site is not live and I decided to wait for the new password api.

I have this code working for passwords in plain text:

<?php
$dbAdapter = Zend_Db_Table::getDefaultAdapter();
$authAdapter = new Zend_Auth_Adapter_DbTable($dbAdapter);

$authAdapter->setTableName('account')
    ->setIdentityColumn('account_id')
    ->setCredentialColumn('account_password');

// Get our authentication adapter and check credentials
$adapter = $authAdapter;
$adapter->setIdentity($values['account_id']);
$adapter->setCredential($values['password']);

$auth = Zend_Auth::getInstance();
$result = $auth->authenticate($adapter);
if ($result->isValid()) {
    $user = $adapter->getResultRowObject();
    $auth->getStorage()->write($user);
    return true;
}
return false;

According to docs I should implement my own adapter and probably just change to make use of password_verify().

I'm missing the big picture here to how everything is working together.

My question is:

  1. Witch object should I modify? $authAdaper or $auth

Any high level (or low level :D) example code would be appreciated.

All best Adam

adamnyberg
  • 58
  • 1
  • 8

2 Answers2

1

If you are looking to modify the way in which your authentication operates by adding the password_hash encryption then you will need to do so within PHP.

As you still wish to use database authentication I think recreating this as a new adapter would be overkill. You could however, extend the current database adapter, such as:

class My_Auth_Adapter_DbTable extends Zend_Auth_Adapter_DbTable
{
  public function setCredential($credential)
  {
    $this->_credential = password_hash($credential);
    return $this;
  }
}

This means that any password provided to the adapter will always be encrypted with the password_hash function.

This could however be acomplished outside the adapter by hashing the password prior to the call to setCredential.

$options  = array('salt' => $config->passwordSalt);
$hashPassword = password_hash($plainTextPassword, PASSWORD_BCRYPT, $options);
$adpater->setCredential($hashPassword);

This method will allow you to modify the optional parameters before passing to the adapter.

Lastly, it is worth mentioning that the setCredentialTreatment method is normally used to provided password encryption, which is performed within the SQL statement (meaning you will need to use the MySQL commands and not password_hash).

$authAdapter->setTableName('user')
      ->setIdentityColumn('email')
      ->setCredentialColumn('password')
      ->setCredentialTreatment(sprintf("MD5(CONCAT(?,'%s'))", $config->passwordSalt));
AlexP
  • 9,906
  • 1
  • 24
  • 43
  • $adpater->setCredential($hashPassword); by doing this way I need to store the salt separately in db? – adamnyberg Jul 03 '13 at 10:55
  • Don't I need to make use of password_verify() anywhere?? – adamnyberg Jul 03 '13 at 11:01
  • If you want to use one salt for all passwords then you can define it in your `application.ini`. `$config->passwordSalt` is just a string, `$config` being a `Zend_Config` instance. – AlexP Jul 03 '13 at 11:07
  • You wont need to use `password_verify` as the password matching will be done in the SQL statement. You will however **need** to hash all your passwords in the database, using the same salt and hash algorithm you use when authenticating. – AlexP Jul 03 '13 at 11:09
  • so none of your solutions will work if I don't want to make a separate salt? According to [this](http://stackoverflow.com/questions/14992367/using-php-5-5s-password-hash-and-verify-function-am-i-doing-it-right#comment21070137_14992543) you should let the new password api take care of the salt. – adamnyberg Jul 03 '13 at 11:35
  • Yes, option 1 or option 2 both work. If You create the hashed password using `password_hash` it will return a hash string...regardless of what salt you have used (internal or one you provide). This is then what the adapter will compare against in the database. Providing the password in the database are already hashed it is a simple comparision of hash password to hashed password. i.e. `WHERE password = '$hashedPassword` – AlexP Jul 03 '13 at 11:46
  • option 2 is now tested and now working but I'm concerned that using the same salt for all passwords and salt in clear text in my config file could open up for vulnerabilities. – adamnyberg Jul 03 '13 at 12:04
  • You **do need** to use ̀password_verify()` as SQL cannot compare hashes created by `password_hash()` as 2 hashes for the same password are not always equals (at least not with BCRYPT or ARGON2). – CDuv Sep 08 '20 at 09:31
0

Hashes created by password_hash() needs to be compared via ̀password_verify()` because 2 hashes for the same password are not always equals (at least not with BCRYPT or ARGON2).

<?php
$pass = 'foo';
var_dump(password_hash($pass, PASSWORD_BCRYPT) === password_hash($pass, PASSWORD_BCRYPT));
// bool(false)
var_dump(password_verify($pass, password_hash($pass, PASSWORD_BCRYPT)));
// bool(true)

Someone (s7anley) did a Zend_Auth_Adapter_DbTable extension that uses password_verify(), here it is (for reference):

<?php

class Base_Auth_Adapter_BcryptDbTable extends Zend_Auth_Adapter_DbTable
{
    /**
     * @inheritdoc
     */
    protected function _authenticateCreateSelect()
    {
        $dbSelect = clone $this->getDbSelect();
        $dbSelect->from($this->_tableName)
            ->where($this->_zendDb->quoteIdentifier($this->_identityColumn, true) . ' = ?', $this->_identity);

        return $dbSelect;
    }

    /**
     * @inheritdoc
     */
    protected function _authenticateValidateResult($resultIdentity)
    {
        $passwordCheck = password_verify($this->_credential, $resultIdentity[$this->_credentialColumn]);

        if (!$passwordCheck) {
            $this->_authenticateResultInfo['code'] = Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID;
            $this->_authenticateResultInfo['messages'][] = 'Supplied credential is invalid.';
            return $this->_authenticateCreateAuthResult();
        }

        $this->_resultRow = $resultIdentity;

        $this->_authenticateResultInfo['code'] = Zend_Auth_Result::SUCCESS;
        $this->_authenticateResultInfo['messages'][] =  'Authentication successful.';
        return $this->_authenticateCreateAuthResult();
    }
}

The class name says "Bcrypt" but it will work just fine with any algorithm supported by password_hash().

You can use it like that:

$authAdapter = new Base_Auth_Adapter_BcryptDbTable($databaseAdapter, 'users', 'login', 'password');
$authAdapter
    ->setIdentity('my_username')
    ->setCredential('my_password') // "clear" password
    // ->setCredentialTreatment(null) // Can't set any treatment on password (would be ignored)
;
// For any additional filtering of returned rows, use getDbSelect()
$authAdapter->getDbSelect()->where('active = "TRUE"');
CDuv
  • 2,098
  • 3
  • 22
  • 28