How does PHP's password_hash generate the salt?

后端 未结 1 408
说谎
说谎 2021-01-01 06:59

Hello as you may know PHP recently introduced password_hash built-in in latest versions. The documentation says:

If omitted, a random salt will be cre

相关标签:
1条回答
  • 2021-01-01 07:16

    The salt is created randomly. They should be statistically unique.

    To see how, check out the C source code.

    On Windows, it will attempt to use php_win32_get_random_bytes() to generate the salt:

    BYTE *iv_b = (BYTE *) buffer;
    if (php_win32_get_random_bytes(iv_b, raw_length) == SUCCESS) {
        buffer_valid = 1;
    }
    

    On Linux, it will attempt to read /dev/urandom to generate the salt:

    int fd, n;
    size_t read_bytes = 0;
    fd = open("/dev/urandom", O_RDONLY);
    if (fd >= 0) {
        while (read_bytes < raw_length) {
            n = read(fd, buffer + read_bytes, raw_length - read_bytes);
            if (n < 0) {
                break;
            }
            read_bytes += (size_t) n;
        }
        close(fd);
    }
    if (read_bytes >= raw_length) {
        buffer_valid = 1;
    }
    

    Then, after those two, if the buffer is not valid (not full, it could be partial), it uses rand() to fill it out. Note that in practice this should never happen, it's just a fallback:

    if (!buffer_valid) {
        for (i = 0; i < raw_length; i++) {
            buffer[i] ^= (char) (255.0 * php_rand(TSRMLS_C) / RAND_MAX);
        }
    }
    

    Now, if C isn't your cup of tea, the same logic and algorithms are implemented in PHP in my compat library:

    $buffer = '';
    $raw_length = (int) ($required_salt_len * 3 / 4 + 1);
    $buffer_valid = false;
    if (function_exists('mcrypt_create_iv')) {
        $buffer = mcrypt_create_iv($raw_length, MCRYPT_DEV_URANDOM);
        if ($buffer) {
            $buffer_valid = true;
        }
    }
    if (!$buffer_valid && function_exists('openssl_random_pseudo_bytes')) {
        $buffer = openssl_random_pseudo_bytes($raw_length);
        if ($buffer) {
            $buffer_valid = true;
        }
    }
    if (!$buffer_valid && is_readable('/dev/urandom')) {
        $f = fopen('/dev/urandom', 'r');
        $read = strlen($buffer);
        while ($read < $raw_length) {
            $buffer .= fread($f, $raw_length - $read);
            $read = strlen($buffer);
        }
        fclose($f);
        if ($read >= $raw_length) {
            $buffer_valid = true;
        }
    }
    if (!$buffer_valid || strlen($buffer) < $raw_length) {
        $bl = strlen($buffer);
        for ($i = 0; $i < $raw_length; $i++) {
            if ($i < $bl) {
                $buffer[$i] = $buffer[$i] ^ chr(mt_rand(0, 255));
            } else {
                $buffer .= chr(mt_rand(0, 255));
            }
        }
    }
    

    The only difference is that the PHP version will use mcrypt or openssl if either is installed...

    0 讨论(0)
提交回复
热议问题