This article analyzes problems related to random number generation used for cryptography purposes. PHP 5 does not provide an easy mechanism for generating cryptographically strong random numbers, while PHP 7 solves this by introducing a couple of CSPRNG functions.

What Is a CSPRNG?

Quoting Wikipedia, a Cryptographically Secure Pseudorandom Number Generator (CSPRNG) is a pseudo-random number generator (PRNG) with properties that make it suitable for use in cryptography.

A CSPRNG could be mainly useful for:

Key generation (e.g. generation of complicated keys)

Creating random passwords for new user accounts

Encryption systems

A central aspect to keeping a high security level is the high quality of randomness.

CSPRNG in PHP 7

PHP 7 introduces two new functions that can be used for CSPRNG: random_bytes and random_int .

The random_bytes function returns a string and accepts as input an int representing the length in bytes to be returned.

Example:

$bytes = random_bytes ( '10' ); var_dump ( bin2hex ( $bytes )); //possible ouput: string(20) "7dfab0af960d359388e6"

The random_int function returns an integer number within the given range.

Example:

var_dump ( random_int ( 1 , 100 )); //possible output: 27

Behind the Scenes

The sources of randomness of the above functions are different depending on the environment:

On Windows, CryptGenRandom() will always be used.

will always be used. On other platforms, arc4random_buf() will be used if it is available (true on BSD derivatives or systems with libbsd).

will be used if it is available (true on BSD derivatives or systems with libbsd). Failing the above, a Linux getrandom(2) syscall will be used.

If all else fails /dev/urandom will be used as the final fallback.

If none of the above sources are available, then an Error will be thrown.

A Simple Test

A good random number generation system assures the right “quality” of generations. To check this quality, a battery of statistical tests is often performed. Without delving into complex statistical topics, comparing a known behavior with the result of a number generator can help in a quality evaluation.

One easy test is the dice game. Assuming the odds of rolling a six with one die are one in six, if I roll three dice at the same time 100 times, the expected values for 0, 1, 2, and 3 sixes are roughly:

0 sixes = 57.9 times

1 sixes = 34.7 times

2 sixes = 6.9 times

3 sixes = 0.5 times

Here is the code to reproduce the dice roll 1.000.000 times:

$times = 1000000 ; $result = []; for ( $i = 0 ; $i < $times ; $i ++){ $dieRoll = array ( 6 => 0 ); //initializes just the six counting to zero $dieRoll [ roll ()] += 1 ; //first die $dieRoll [ roll ()] += 1 ; //second die $dieRoll [ roll ()] += 1 ; //third die $result [ $dieRoll [ 6 ]] += 1 ; //counts the sixes } function roll (){ return random_int ( 1 , 6 ); } var_dump ( $result );

Testing the code above with PHP7 random_int and the simple rand function might produce:

Sixes expected random_int rand 0 579000 579430 578179 1 347000 346927 347620 2 69000 68985 69586 3 5000 4658 4615

To see a better comparison between rand and random_int we can plot the results in a graph applying a formula to increase the differences between values: php result - expected result / sqrt(expected) .

The resulting graph will be:



(values close to zero are better)

Even if the three sixes combination doesn’t perform well, and the test is too easy for a real application we can clearly see that random_int performs better than rand .

Furthermore, the security of an application is increased by the absence of predictable, repeatable behaviors in the random number generator adopted.

What about PHP 5?

By default, PHP 5 does not provide any strong pseudo-random number generators. In reality, there are some options like openssl_random_pseudo_bytes() , mcrypt_create_iv() or directly use the /dev/random or /dev/urandom devices with fread() . There are also packages like RandomLib or libsodium.

If you want to start using a good random number generator and at the same time be PHP 7-ready you can use the Paragon Initiative Enterprises random_compat library. The random_compat library allows the use of random_bytes() and random_int() in your PHP 5.x project.

The library can be installed via Composer:

composer require paragonie / random_compat

require 'vendor/autoload.php' ; $string = random_bytes ( 32 ); var_dump ( bin2hex ( $string )); // string(64) "8757a27ce421b3b9363b7825104f8bc8cf27c4c3036573e5f0d4a91ad2aaec6f" $int = random_int ( 0 , 255 ); var_dump ( $int ); // int(81)

The random_compat library uses a different preference order compared to the PHP 7 one:

fread() /dev/urandom if available mcrypt_create_iv($bytes, MCRYPT_CREATE_IV) COM('CAPICOM.Utilities.1')->GetRandom() openssl_random_pseudo_bytes()

For more information about why this order has been used, I suggest reading the documentation.

A simple use of the library to generate a password can be:

$passwordChar = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' ; $passwordLength = 8 ; $max = strlen ( $passwordChar ) - 1 ; $password = '' ; for ( $i = 0 ; $i < $passwordLength ; ++ $i ) { $password .= $passwordChar [ random_int ( 0 , $max )]; } echo $password ; //possible output: 7rgG8GHu

Conclusion

You should always apply a cryptographically secure pseudo-random number generator, and the random_compat lib provides a good implementation for this.

If you want to use a reliable random data source, as you saw in the article, the suggestion is to start as soon as possible with random_int and random_bytes .

Questions or comments? Leave them below!

Further Reading

Description Link Die Hard Test https://en.wikipedia.org/wiki/Diehard_tests Chi-square test with dice example http://bit.ly/1Mrptf5 Kolmogorov-Smirnov Test https://en.wikipedia.org/wiki/Kolmogorov-Smirnov_test Spectral Test http://random.mat.sbg.ac.at/tests/theory/spectral/ RaBiGeTe test suite http://cristianopi.altervista.org/RaBiGeTe_MT Random Number Generation In PHP (2011) http://blog.ircmaxell.com/2011/07/random-number-generation-in-php.html Testing RNG part 1 and 2 http://ubm.io/1Ot46vL http://ubm.io/1VNzh3N

Acknowledgements

Many thanks to the following peer reviewers for their help with this article!