4

Okay i am sitting here since hours scratching my head at this issue and i cannot figure out what is wrong. I am trying to encrypt a password via a random salt with crypt but when i try to login the has is always wrong.

Let me walk you through the script:

$cost = 10;
$salt = strtr(base64_encode(mcrypt_create_iv(16, MCRYPT_DEV_URANDOM)), '+', '.');
$salt = sprintf("$2y$%02d$", $cost) . $salt;
$hash = crypt($password, $salt);

echo $hash;
echo crypt($password, $hash);

Outputs the following with password as 'asdfgh':

$2y$10$865uru.sXJheD9TQKLDnZuTZfpAXv83UDuaSFfb.G2qIxBzEb1pOi
$2y$10$865uru.sXJheD9TQKLDnZuTZfpAXv83UDuaSFfb.G2qIxBzEb1pOi

The hash in the database looks like this:

$2y$10$865uru.sXJheD9TQKLDnZuTZfpAXv83UDuaSFfb.G2qIxBzEb1pOi

For the login script we have the following code for testing purposes:

echo $data->hash . '<br>';
echo crypt('asdfgh', $data->hash) . '<br>';
echo crypt('asdfgh', '$2y$10$865uru.sXJheD9TQKLDnZuTZfpAXv83UDuaSFfb.G2qIxBzEb1pOi');

And that outputs the following:

$2y$10$865uru.sXJheD9TQKLDnZuTZfpAXv83UDuaSFfb.G2qIxBzEb1pOi
$2y$10$865uru.sXJheD9TQKLDnZuRRPJQwjWh2PGgtntpcsnRaGzvv5Sfte
$2y$10$865uru.sXJheD9TQKLDnZuRRPJQwjWh2PGgtntpcsnRaGzvv5Sfte

While the database string is still correct, and even by passing the correct string manually to the function the generated hash is different. I am out of solutions ...

If anyone could help me i would very much appreciate it.

PHP Version 5.4.16 on Windows

UPDATE: Here is the updated snippets with the salt:

$cost = 10;
$salt = strtr(base64_encode(mcrypt_create_iv(16, MCRYPT_DEV_URANDOM)), '+', '.');
$salt = sprintf("$2y$%02d$", $cost) . $salt;
$hash = crypt($password, $salt);

$data = array(
    'id' => '',
    'username' => $username,
    'hash' => $hash,
    'email' => $email,
    'salt' => $salt,
);

$this->mdl_registration->_insert($data);
$this->load->view('registration_submit');

For the login script:

function check_login($password) {
    $username = $this->input->post('username');
    $result = $this->get_where_custom('username', $username);
    foreach($result->result() as $data) {
        echo $data->hash . '<br>';
        echo crypt('asdfgh', $data->salt) . '<br>';
        $test = crypt($password, $data->salt);
        if($test == $data->hash) {
            return TRUE;
        }
        else {
            $this->form_validation->set_message('check_login', 'Invalid Username and / or Password');
            return FALSE;
        }
    }
}

The echos for testing purposes return the following:

$2y$10$ZgbOXM18lArDu/u/Ftsdr.t7VPnLsqLJdC2Dum8pl/flW8LmnnUoS
$2y$10$ZgbOXM18lArDu/u/Ftsdr.s5N5juHB/zq/5SN/7oFAjn9CZKjI9H6
Alexander Yancharuk
  • 13,817
  • 5
  • 55
  • 55
Sepiksu
  • 41
  • 3
  • You are using a different salt the 2nd time, in the first crypt you have `$salt = strtr(base64_encode(mcrypt_create_iv(16, MCRYPT_DEV_URANDOM)), '+', '.'); $salt = sprintf("$2y$%02d$", $cost) . $salt;` as salt, the 2nd time you use your hash ($hash) as salt – Elpy Aug 05 '13 at 18:42
  • Meant `echo crypt('asdfgh', $salt);` -> Just as @Elpy has mentioned also. You should store the `$salt` in the database when the user registers, and afterwards, retrieve that `$salt` on login attempts, hash that with the password and compare the hashes. – Mattiavelli Aug 05 '13 at 18:46
  • $data->hash is coming out of the database where it is stored in the user table. I am trying to validate the password via if(crypt($password, $data->hash) == $data->hash) which always returns false because of the different outcome. – Sepiksu Aug 05 '13 at 18:48
  • I tried it with the salt as well, still a different outcome than the original one. – Sepiksu Aug 05 '13 at 18:49
  • How can `echo $hash;` & `echo crypt($password, $hash);` output the same, in the first snippet? `$password` not set? – Daniel W. Aug 05 '13 at 20:12
  • because that is how crypt works,. (or should work). Even the official manual page shows it like that. – Sepiksu Aug 06 '13 at 05:04
  • uff.. finally something new I learned and Im suprised I don't understand it :D Like getting the same result by using different salts.. http://codepad.org/PwBSMV0O – Daniel W. Aug 06 '13 at 08:34
  • You're supposed to do `$test = crypt($password, $data->hash);` and not `$test = crypt($password, $data->salt);`. But you know that. And as for the testing output (the fifth code-box from the top).. I can't think of any other explanation than that you had the password somewhat different. – Smuuf Aug 06 '13 at 11:23
  • Password is exactly the same. I just tried it with the new password_hash functions of PHP 5.5 and it still produces different results. – Sepiksu Aug 07 '13 at 15:52
  • Try to extract the essence of your problem (while still maintaining the errorneous behaviour) and put it in here: http://phpfiddle.org/ and share it .. because this is just wrong and interesting at the same time :) – Smuuf Aug 10 '13 at 08:32

2 Answers2

0

Well, my answer doesn't actually solve why this code isn't working. I thought it was due to the $salt not being passed, but apparently crypt() accounts for this so long as the salt is within the provided $hash...

Anyhow, I can suggest an alternative method to accomplish this. Feel free to ignore this and wait for a solution, or adapt this. Up to you.

$password = 'asdfgh';
$salt = dechex(mt_rand(0, 2147483647)) . dechex(mt_rand(0, 2147483647));
$hash = hash('sha256', $password . $salt);
for($round = 0; $round < 60000; $round++)
{
    $hash = hash('sha256', $hash . $salt);
}

This creates the salt slightly differently, uses a different hashing algorithm and also iterates through the hash 60,000 times (relatively short for a user 1-2 seconds).

Store this $salt in the database along with the $hash.

Now, for the login script just include this snippet...

(assuming $password is 'asdfgh')

$newHash = hash('sha256', $password . $salt);
for($round = 0; $round < 60000; $round++)
{
    $newHash = hash('sha256', $newHash . $salt);
}
if($newHash===$data->hash){
  //Match!
}

I tried this with several different data sets and the hashes always matched up, so you could use this.

Or you can wait for a real security expert to come explain why your code didn't work :)

Hope this helped!

Mattiavelli
  • 888
  • 2
  • 9
  • 22
  • Sadly it does not :/ Even when i use the salt which i store in the database, i get a completely different result. The interesting thing is also, if backward decryption with the hash isn't supposed to work why does it work in the example above but not in the login script a bit lower? I am pretty confused at this point. – Sepiksu Aug 05 '13 at 19:01
  • Can you elaborate on which examples/snippets you're referring to with `backward decryption`? Also, can you please post your updated results from attempting it with the salt? On my end this works without a hitch. – Mattiavelli Aug 05 '13 at 19:07
  • $hash = crypt($password, $salt); echo $hash; echo crypt($password, $hash); Both has the same output: – Sepiksu Aug 05 '13 at 19:09
  • Good point. You're actually right, it's a feature apparently built into crypt to account for multiple hashing algorithms that it uses. [Source](http://php.net/manual/en/function.crypt.php). Updating answer.. – Mattiavelli Aug 05 '13 at 19:16
  • i added my update with the salt test now. As you can see there is a different result again. – Sepiksu Aug 05 '13 at 19:18
0

Your salt has the wrong format.

According to the docs:

CRYPT_BLOWFISH - Blowfish hashing with a salt as follows: "$2a$", "$2x$" or "$2y$", a two digit cost parameter, "$", and 22 characters from the alphabet "./0-9A-Za-z".

Your salt has a) invalid characters (= as fill character at the end of the base64) and b) is too long (24 chars instead of 22).

This might be the cause of your problem.

apfelbox
  • 2,625
  • 2
  • 24
  • 26