Randomness in PHP - Do You Feel Lucky?
Share:
Free JavaScript Book!
Write powerful, clean and maintainable JavaScript.RRP $11.95
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. - On other platforms,
arc4random_buf()
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 availablemcrypt_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!
Nicola Pietroluongo is a software engineer with many years of experience, open source enthusiast, now creating and contributing to awesome PHP web projects.
New books out now!
Get practical advice to start your career in programming!
Master complex transitions, transformations and animations in CSS!
Latest Remote Jobs



