Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does PHP's password_hash generate the salt?

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 created and the default cost will be used.

The question is what kind of method does it use to add the salt?

I'm interested because I'd like to know if the salt is created randomly so that when I store my hashed passwords they are always unique.

like image 917
Sandro Antonucci Avatar asked Jan 31 '26 19:01

Sandro Antonucci


1 Answers

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...

like image 158
ircmaxell Avatar answered Feb 03 '26 08:02

ircmaxell



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!