Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
192 views
in Technique[技术] by (71.8m points)

php - bcrypt and randomly generated salts

So I was experimenting with bcrypt. I have a class(shown below, which I got from http://www.firedartstudios.com/articles/read/php-security-how-to-safely-store-your-passwords) in which there are 3 functions. 1st one is to generate a random Salt, the 2nd to generate a hash using the 1st generated Salt and the last one is to verify the supplied password by comparing it with the hashed password.

<?php
/* Bcrypt Example */
class bcrypt {
    private $rounds;
    public function __construct($rounds = 12) {
        if(CRYPT_BLOWFISH != 1) {
            throw new Exception("Bcrypt is not supported on this server, please see the following to learn more: http://php.net/crypt");
        }
        $this->rounds = $rounds;
    }

    /* Gen Salt */
    public function genSalt() {
        /* openssl_random_pseudo_bytes(16) Fallback */
        $seed = '';
        for($i = 0; $i < 16; $i++) {
            $seed .= chr(mt_rand(0, 255));
        }
        /* GenSalt */
        $salt = substr(strtr(base64_encode($seed), '+', '.'), 0, 22);
        /* Return */
        return $salt;
    }

    /* Gen Hash */
    public function genHash($password) {
        /* Explain '$2y$' . $this->rounds . '$' */
            /* 2a selects bcrypt algorithm */
            /* $this->rounds is the workload factor */
        /* GenHash */
        $hash = crypt($password, '$2y$' . $this->rounds . '$' . $this->genSalt());
        /* Return */
        return $hash;
    }

    /* Verify Password */
    public function verify($password, $existingHash) {
        /* Hash new password with old hash */
        $hash = crypt($password, $existingHash);

        /* Do Hashs match? */
        if($hash === $existingHash) {
            return true;
        } else {
            return false;
        }
    }
}
/* Next the Usage */
/* Start Instance */
$bcrypt = new bcrypt(12);

/* Two create a Hash you do */
echo 'Bcrypt Password: ' . $bcrypt->genHash('password');

/* Two verify a hash you do */
$HashFromDB = $bcrypt->genHash('password'); /* This is an example you would draw the hash from your db */
echo 'Verify Password: ' . $bcrypt->verify('password', $HashFromDB);
?>

Now if I generate a hash with 'password' for example, I get a hashed password, which took the randmonly generated Salt. Next if I enter 'password' again and use the verify function ,I get true meaning that the passwords match. If I enter wrong password, I get false. My question is how is this possible? What about the randomly generated Salt? How come that is not having any effect?

Question&Answers:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Have a good look at the values you're dealing with. The random salt generated will be, say:

abcdefg...

What is fed into crypt looks like this:

crypt($password, '$2y$10$abcdefg...')
                   |  |    |
                   |  |    +- the salt
                   |  +- the cost parameter
                   +- the algorithm type

The result looks like:

$2y$10$abcdefg...123456789...
 |  |    |        |
 |  |    |        +- the password hash
 |  |    +- the salt
 |  +- the cost parameter
 +- the algorithm type

In other words, the first part of the resulting hash is the same as the original input into the crypt function; it contains the algorithm type and parameters, the random salt and the hash result.

Input:  $password + $2y$10$abcdefg...
Output:             $2y$10$abcdefg...123456789...
                    ^^^^^^^^^^^^^^^^^
                   first part identical

When you confirm a password, you need the same, original salt again. Only with the same salt will the same password hash to the same hash. And it's still there in the hash, in a format that can be passed to crypt as is to repeat the same operation as when the hash was generated. That's why you need to feed both the password and hash into the validation function:

crypt($passwordToCheck, '$2y$10$abcdefg...123456789...')

crypt takes the first defined number of characters, up to and including abcdefg... and throws the rest away (that's why the salt needs to be a fixed number of characters). Therefore it equals the same operation as before:

crypt($passwordToCheck, '$2y$10$abcdefg...')

And will generate the same hash, if and only if $passwordToCheck is the same.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...