Developers Club geek daily blog

2 years, 9 months ago
Криптографическая рандомизация в PHP

In this article we will analyze the problems relating to random number generation, used in cryptography. PHP5 does not provide a simple generation engine of cryptoresistant random numbers while PHP7 solves this problem by introduction of CSPRNG functions.

What is CSPRNG?


Quoting Wikipedia, cryptographic the resistant pseudorandom number generator (English Cryptographically secure pseudorandom number generator, CSPRNG) is a pseudorandom number generator with the certain properties allowing to use it in cryptography.

CSPRNG is generally used for the following purposes:
  • Key generation (including, generation of public/private of keys)
  • Creation of accidental passwords for accounts of users
  • Encryption systems

The main aspect of saving of the high level of safety is high quality of randomness.

CSPRNG in PHP7


PHP7 enters two new functions which can be used for CSPRNG: random_bytes and random_int.

Function random_bytes returns a line and accepts as input parameters int, setting length (in bytes) a returned value:

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

random_int returns an integral number in the set range:

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

Off-screen


Sources of randomness of above-mentioned functions differ depending on Wednesday:
  • In Windows it will be always used CryptGenRandom();
  • On other platforms — on condition of availability it will be involved arc4random_buf() (it is right in case of BSD-derivative systems or systems with libbsd).
  • In case of unavailability above-designated, in Linux system will be used getrandom(2).
  • If all this fails, as final attempt of PHP will try to involve /dev/urandom.
  • At impossibility to use these sources the error will be thrown out.

Simple test


The good system of generation accidental number is defined by "quality" of generation. That to check it often the set of the statistical tests allowing is used without penetrating into a difficult subject of statistics, to compare the known reference behavior to result of the generator and to help with an assessment of its quality.

One of the simplest tests — dicing. Estimated probability of loss of the six at one bone — one to six, at the same time, if I throw three bones of 100 times then expected loss 1, 2 and 3kh a shesterok approximately such:
  • 0 shesterok = 57.9 times
  • 1 six = 34.7 times
  • 2 six = 6.9 times
  • 3 six = 0.5 times

Here a code for reproduction of a throw of bones of 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);

Pro-race of a code in PHP7 c use random_int and simple rand will issue the following results:
Six The expected result random_int rand
0 579000 579430 578179
1 347000 346927 347620
2 69000 68985 69586
3 5000 4658 4615

For the best comparison rand and random_int let's construct the diagram of results, applying a formula: результат PHPожидаемый результат / sqrt(ожидаемый результат).

The diagram will look so (the closer to zero, the better):
график test random

Even despite bad result with three shesterka and all simplicity of the test, we see clear superiority random_int over rand.

And what about PHP5?


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

If you want to begin to use a good random number generator and at the same time for the present are not ready to transition to PHP7, you can use a bibliteka random_compat from Paragon Initiative Enterprises. She allows to use random_bytes() and random_int() in PHP 5.kh projects.

The library can be set through 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)

In comparison with PHP7, random_compat uses several other priorities:
  1. fread() /dev/urandom if it is available
  2. mcrypt_create_iv($bytes, MCRYPT_CREATE_IV)
  3. COM('CAPICOM.Utilities.1')->GetRandom()
  4. openssl_random_pseudo_bytes()


Additional information on that why this order is used you can read in documentation.

Example of generation of the password with use of 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 result


You always have to use cryptographic resistant pseudorandom number generators, and random_compat is a good solution for this purpose.

If the reliable source of accidental data is necessary for you, then look aside random_int and random_bytes.

Links on a subject



This article is a translation of the original post at habrahabr.ru/post/272509/
If you have any questions regarding the material covered in the article above, please, contact the original author of the post.
If you have any complaints about this article or you want this article to be deleted, please, drop an email here: sysmagazine.com@gmail.com.

We believe that the knowledge, which is available at the most popular Russian IT blog habrahabr.ru, should be accessed by everyone, even though it is poorly translated.
Shared knowledge makes the world better.
Best wishes.

comments powered by Disqus