Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generate cryptographically secure random numbers in php

PHP's rand() function doesn't give good random numbers. So I started to use mt_rand() which is said to give better results. But how good are these results? Are there any methods to improve them again?

My idea:

function rand_best($min, $max) {
    $generated = array();
    for ($i = 0; $i < 100; $i++) {
        $generated[] = mt_rand($min, $max);
    }
    shuffle($generated);
    $position = mt_rand(0, 99);
    return $generated[$position];
}

This should give you "perfect" random numbers, shouldn't it?

like image 479
caw Avatar asked Jun 24 '09 23:06

caw


People also ask

What makes a random number generator cryptographically secure?

A PRNG is said to be cryptographically secure if, assuming that it operates over a wide enough unknown n-bit key, its output is computationally indistinguishable from uniformly random bits.

How do I generate a random number in PHP?

The rand() function generates a random integer. Example tip: If you want a random integer between 10 and 100 (inclusive), use rand (10,100). Tip: As of PHP 7.1, the rand() function has been an alias of the mt_rand() function.

Is math random cryptographically secure?

Note: Math. random() does not provide cryptographically secure random numbers. Do not use them for anything related to security.

How do you generate a non repeating random number in PHP?

php session_start(); if (! isset($_SESSION['numbers'])) { $_SESSION['numbers']="*"; //---create the session variable } function get_number() { $i = 0; do { $num=rand(1,20); //---generate a random number if (! strstr($_SESSION['numbers'],"*".


3 Answers

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.

like image 104
coobird Avatar answered Oct 07 '22 18:10

coobird


Quick answer:

In a new PHP7 there is a finally a support for a cryptographically secure pseudo-random integers.

int random_int ( int $min , int $max )

There is also a polyfill for PHP5x.

Longer answer


There is no perfect random number generator, and computers use pseudorandom number generator to create sequences that looks random. The sequences look random (and pass some randomness tests) but because there is some algorithm to generate it, you can repeat algorithm with absolutely the same states and get the same result.

The same advice as with cryptography "do not invent your own cypher" can be translated to random number generators and mean that you can not just get a lot of random number generators combined together and get expect to get a better generator.


One of the subsets of random number generators is cryptographically secure random number generators:

The requirements of an ordinary PRNG are also satisfied by a cryptographically secure PRNG, but the reverse is not true. CSPRNG requirements fall into two groups: first, that they pass statistical randomness tests; and secondly, that they hold up well under serious attack, even when part of their initial or running state becomes available to an attacker

So this is pretty close to your definition of "perfect". One more time under no condition (except of learning how to do cryptography) you should try to implement one of that algorithms and use it in your system.


But luckily PHP7 has it implemented,

int random_int ( int $min , int $max )

Generates cryptographic random integers that are suitable for use where unbiased results are critical (i.e. shuffling a Poker deck).

The sources of random are as follows:

  • On Windows CryptGenRandom() is used exclusively
  • arc4random_buf() is used if it is available (generally BSD specific)
  • /dev/arandom is used where available
  • The getrandom(2) syscall (on newer Linux kernels)
  • /dev/urandom is used where none of the above is available

This makes all the previous answers obsolete (and some deprecated).

like image 35
Salvador Dali Avatar answered Oct 07 '22 19:10

Salvador Dali


I'm not sure that what you've done "improves" the randomness. From what I can understand you generate 100 random numbers and then randomly pick one of them.

From what I can remember from my probability course, this probably doesn't increase the randomness, as if there is an underlying bias in the generator function (mt_rand()), then it will still be reflected somehow in the output.

like image 17
Peter Avatar answered Oct 07 '22 18:10

Peter