Generate Cryptographically Secure Random Numbers in PHP

Secure random number generation in PHP

You can also consider using OpenSSL openssl_random_pseudo_bytes, it's available since PHP 5.3.

 string openssl_random_pseudo_bytes ( int $length [, bool &$crypto_strong ] )

Generates a string of pseudo-random bytes, with the number of bytes determined by the length parameter.
It also indicates if a cryptographically strong algorithm was used to produce the pseudo-random bytes, and does this via the optional crypto_strong parameter. It's rare for this to be FALSE, but some systems may be broken or old.

http://www.php.net/manual/en/function.openssl-random-pseudo-bytes.php

Since PHP 7 there is also random_bytes function available

string random_bytes ( int $length )

http://php.net/manual/en/function.random-bytes.php

Generate cryptographically secure random numbers in php

Pseudorandom number generators (PRNG) are very complex beast.

There are no real "perfect" random number generators -- in fact the best that can be done from mathematical functions are pseudorandom -- they seem random enough for most intents and purposes.

In fact, performing any additional actions from a number returned by a PRNG doesn't really increase its randomness, and in fact, the number can become less random.

So, my best advice is, don't mess around with values returned from a PRNG. Use a PRNG that is good enough for the intended use, and if it isn't, then find a PRNG that can produce better results, if necessary.

And frankly, it appears that the mt_rand function uses the Mersenne twister, which is a pretty good PRNG as it is, so it's probably going to be good enough for most casual use.

However, Mersenne Twister is not designed to be used in any security contexts. See this answer for a solution to use when you need randomness to ensure security.

Edit

There was a question in the comments why performing operations on a random number can make it less random. For example, some PRNGs can return more consistent, less random numbers in different parts of the bits -- the high-end can be more random than the low-end.

Therefore, in operations where the high-end is discarded, and the low end is returned, the value can become less random than the original value returned from the PRNG.

I can't find a good explanation at the moment, but I based that from the Java documentation for the Random.nextInt(int) method, which is designed to create a fairly random value in a specified range. That method takes into account the difference in randomness of the parts of the value, so it can return a better random number compared to more naive implementations such as rand() % range.

Generate a Secure Random Integer in Range with PHP 5.6

Might I introduce you to random_compat, which polyfills random_bytes() and random_int() in PHP 5 projects? (Sidenote: among other projects, it's being picked up by Wordpress in 4.4.)

function secure_rand($min, $max)
{
return (unpack("N", openssl_random_pseudo_bytes(4)) % ($max - $min)) + $min;
}

Even if this was doing what you wanted it to do, this is a biased random number generator when ($max - $min) is not an even power of 2.

Cryptographically secure random ASCII-string in PHP

Unfortunately Peter O. deleted his answer after receiving negative attention in a review queue, perhaps because he phrased it as a question. I believe it is legitimate answer so I will reprise it.

One easy solution is to encode your random data into the base64 alphabet using base64_encode(). This will not produce the "full ASCII-range" as you have requested but it will give you most of it. An even larger ASCII range is output by a suitable base85 encoder, but php does not have a built-in one. You can probably find plenty of open-source base85 encoders for php though. In my opinion the decrease in length of base85 over base64 is unlikely to be worth the extra code you have to maintain.

Cryptographically Secure Random String Function

So you want to securely generate random strings in PHP. Neither of the two functions in the question will give you what you want, but the rand() solution is the worst of the two. rand() is not secure, while bin2hex(openssl_random_pseudo_bytes()) limits your output character set.

Also, openssl_random_pseudo_bytes() might not be reliable under extreme conditions or exotic setups.

From what I understand, crypto_strong will only be set to false if RAND_pseudo_bytes() fails to return any data. If OpenSSL is not seeded when it's invoked, it will silently return weak (and possibly predictable) pseudorandom bytes. You have no way, from PHP, to determine if it's random either.

How to generate secure random strings today

If you want a solution that has received substantial review for PHP 5.x, use RandomLib.

$factory = new RandomLib\Factory;
$generator = $factory->getMediumStrengthGenerator();
$randomPassword = $generator->generateString(20, $alphabet);

Alternative solutions

If you'd rather not use RandomLib (even if, purely, because you want to have alternative options available), you can also use random_int() when PHP 7 comes out. If you can't wait until then, take a look at our random_compat project.

If you happen to be using the cryptography library, libsodium, you can generate random numbers like so:

/**
* Depends on the PECL extension libsodium
*
* @link https://stackoverflow.com/a/31498051/2224584
*
* @param int $length How long should the string be?
* @param string $alphabet Contains all of the allowed characters
*
* @return string
*/
function sodium_random_str($length, $alphabet = 'abcdefghijklmnopqrstuvwxyz')
{
$buf = '';
$alphabetSize = strlen($alphabet);
for ($i = 0; $i < $length; ++$i) {
$buf .= $alphabet[\Sodium\randombytes_uniform($alphabetSize)];
}
return $buf;
}

See this answer for example code that uses random_int(). I'd rather not duplicate the effort of updating the code in the future, should the need ever arise.

Is PHP gmp_random_range function from GMP extension cryptographically secure?

As shown in "Random State Initialization", in the GMP documentation, the only random generator algorithms included in GMP are—

  • A Mersenne Twister algorithm,
  • linear congruential generators of various sizes, and
  • a "default algorithm" for "applications with no special requirements", including security requirements.

Curiously, the GMP documentation says in "Random State Seeding" that the "method for choosing a seed is critical if the generated numbers are to be used for important applications, such as generating cryptographic keys", even though none of the algorithms included in GMP are appropriate for cryptographic use.


As it appears, you can use random_bytes or random_int in PHP to generate cryptographic random numbers. The only thing left is to transform the numbers they deliver into arbitrary-precision numbers.* In that sense, GMP appears not to allow custom RNGs (besides the ones it provides) for the gmp_random_range and similar functions. Thus, you will have to transform those random numbers "manually", with the help of GMP's arithmetic functions. I have an article that discusses how to transform random numbers into a variety of distributions. To generate uniform random integers in a given range, the algorithm you need is called RNDINT or RNDINTEXC in that article.

* Where information security is involved, using random numbers as the seed for a noncryptographic RNG is not appropriate since an attacker, given enough random numbers, can then work backwards to derive the seed, even if the seed was generated in a cryptographically secure way.


If your goal is merely to generate a cryptographically random string of characters, you don't even need to go the GMP route. Just build the string one character at a time by calling random_int for each character you want to generate:

  • Build a list of characters allowed to appear in the random string.
  • For each character in the string, call random_int with a max of the list's size (a size which will almost certainly be within the range of integers PHP can handle), then append the character found at the given random index in the list (starting at 0).

What is meant by cryptographically secure?

Computers don't normally do a very good job at calculating a truly random number. This means that the pseudo-random number that a computer calculates might be predictable.

If this random number is then used as a basis for cryptographic key, then the key and so the secured message can be compromised.

A cryptographically secure pseudo random number generator (CSPRNG), is one where the number that is generated is extremely hard for any third party to predict what it might be. This means that cryptographic keys derived from these random numbers are extremely hard to determine making messages secured with such keys safe.

From

https://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator

Ideally, the generation of random numbers in CSPRNGs uses entropy obtained from a high-quality source, generally the operating system's randomness API. However, unexpected correlations have been found in several such ostensibly independent processes. From an information-theoretic point of view, the amount of randomness, the entropy that can be generated, is equal to the entropy provided by the system. But sometimes, in practical situations, more random numbers are needed than there is entropy available. Also the processes to extract randomness from a running system are slow in actual practice. In such instances, a CSPRNG can sometimes be used. A CSPRNG can "stretch" the available entropy over more bits.

PHP Seeded, Deterministic, Cryptographically Secure PRNG (PseudoRandom Number Generator). Is it possible?

Duskwuff asks:

How do you intend to prove that the seed was chosen fairly? A suspicious user can easily claim that you picked a seed that would result in a favorable outcome for specific users, or that you revealed the seed to specific users ahead of time.

Before you investigate solutions, what exactly is the problem you are trying to solve? What is your threat model?


It sounds like you want SeedSpring (version 0.3.0 supports PHP 5.6).

$prng = new \ParagonIE\SeedSpring\SeedSpring('JuxJ1XLnBKk7gPAS');
$byte = $prng->getBytes(16);
\var_dump(bin2hex($byte));

This should always return:

string(32) "76482c186f7c5d1cb3f895e044e3c649"

The numbers should be unbiased, but since it's based off a pre-shared seed, it is not, by strict definition, cryptographically secure.

Keep in mind that SeedSpring was created as a toy implementation/proof of concept rather than an official Paragon Initiative Enterprises open source security solution, so feel free to fork it and tweak it to suit your purposes. (I doubt our branch will ever reach a "stable 1.0.0 release").

(Also, if you're going to accept/award the bounty to any of these answers, Aaron Toponce's answer is more correct. Encrypting the nonce with ECB mode is more performant than encrypting a long stream of NUL bytes with AES-CTR, for approximately the same security benefit. This is one of the extremely rare occasions that ECB mode is okay.)

PHP rand() vs. random_int()

Revisiting the question and seeing there's been an answer given, I find it's only fair that I submit my comments to an answer, seeing they were submitted before.

The manual on PHP 7's random_int() function states:

"Returns a cryptographically secure random integer in the range min to max, inclusive."

  • http://php.net/manual/en/function.random-int.php

and for rand()

*This function does not generate cryptographically secure values" *

  • http://php.net/manual/en/function.rand.php

OP's comment:

"@Fred-ii- thank you. But what does "cryptographically secure pseudo-random" mean? – NDFA"

That can be found in the following links as per my findings:

  • https://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator

Which states:

A cryptographically secure pseudo-random number generator (CSPRNG) or cryptographic pseudo-random number generator (CPRNG)[1] is a pseudo-random number generator (PRNG) with properties that make it suitable for use in cryptography.


  • How does a cryptographically secure random number generator work?

In regards to performance, you will need to run a benchmark yourself.



Related Topics



Leave a reply



Submit