📜 ⬆️ ⬇️

Randomness in PHP7 - Will I get lucky?

PHP

In this article we will analyze the problems related to the generation of random numbers used in cryptography. PHP5 does not provide a simple mechanism for generating crypto-resistant random numbers, while PHP7 solves this problem by introducing CSPRNG functions.

What is CSPRNG?


Quoting Wikipedia , a cryptographically secure pseudorandom number generator (eng. Cryptographically secure pseudorandom number generator, CSPRNG) is a pseudorandom number generator with specific properties that allow it to be used in cryptography.
')
CSPRNG is mainly used for the following purposes:

The main aspect of maintaining high security is the high quality of randomness.

CSPRNG in PHP7


PHP7 introduces two new features that can be used for random_bytes : random_bytes and random_int .

The random_bytes function returns a string and takes as input parameters an int that defines the length (in bytes) of the return value:

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

random_int returns an integer in the specified range:

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

Behind the scenes


Sources of randomness of the above functions differ depending on the environment:

Simple test


A good system for generating random numbers is determined by the "quality" of the generations. To test it, a set of statistical tests is often used, allowing, without delving into the complex topic of statistics, to compare the known reference behavior with the result of the generator and to help in assessing its quality.

One of the easiest tests is a dice game. The estimated probability of a six loss with one bone is one to six, but at the same time, if I roll three dice 100 times, then the expected loss of 1, 2 and 3 six is ​​approximately the following:

Here is the code to play the roll of bones 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); 

Running code in PHP7 using random_int and a simple rand will random_int following results:
SixesExpected Resultrandom_intrand
0579,000579430578179
one347,000346927347620
2690006898569586
3500046584615

For a better comparison of rand and random_int we construct a graph of results using the formula: PHP - / sqrt( ) .

The graph will look like this (the closer to zero, the better):
test random

Even despite the bad result with three sixes and the simplicity of the test, we see a clear advantage of random_int over rand .

What about PHP5?


By default, PHP5 does not provide any strong pseudo-random number generators. But there are actually several options, such as openssl_random_pseudo_bytes() , mcrypt_create_iv() or using /dev/random directly or /dev/urandom with fread() . There are also libraries such as RandomLib or libsodium .

If you want to start using a good random number generator and at the same time are not yet ready to switch to PHP7, you can use the random_compat library from Paragon Initiative Enterprises. It allows using random_bytes() and random_int() in PHP 5.x projects.

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) 

Compared to PHP7, random_compat uses slightly different priorities:
  1. fread() /dev/urandom if available
  2. mcrypt_create_iv($bytes, MCRYPT_CREATE_IV)
  3. COM('CAPICOM.Utilities.1')->GetRandom()
  4. openssl_random_pseudo_bytes()


For more information on why this order is used, you can read the documentation .

An example of generating a password using the library:

 $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 

Short summary


You should always use cryptographically random_compat pseudo-random number generators, and random_compat is a good solution for this.

If you need a reliable source of random data, then look towards random_int and random_bytes .

Related Links


Source: https://habr.com/ru/post/272509/


All Articles