What's the Disadvantage of Mt_Rand

What's the disadvantage of mt_rand?

mt_rand uses the Mersenne Twister algorithm, which is far better than the LCG typically used by rand. For example, the period of an LCG is a measly 232, whereas the period of mt_rand is 219937 − 1. Also, all the values generated by an LCG will lie on lines or planes when plotted into a multidimensional space. Also, it is not only practically feasible, but relatively easy to determine the parameters of an LCG. The only advantage LCGs have is being potentially slightly faster, but on a scale that is completely irrelevant when coding in php.

However, mt_rand is not suitable for cryptographic purposes (generation of tokens, passwords or cryptographic keys) either.

If you need cryptographic randomness, use random_int in php 7. On older php versions, read from /dev/urandom or /dev/random on a POSIX-conforming operating system.

is mt_rand() more secure than rand()

Directly from the docs:

This function does not generate cryptographically secure values, and should not be used for cryptographic purposes. If you need a cryptographically secure value, consider using openssl_random_pseudo_bytes() instead.

mt_rand generates better random numbers than rand, and much faster. But that doesn't make it "secure" in the sense that it should be used for cryptography. Whether it's secure enough for your application is pretty subjective.

Difference between mt_rand() and rand()

Update

Since PHP 7.1 mt_rand has superseded rand completely, and rand was made an alias for mt_rand. The answer below focuses on the differences between the two functions for older versions, and the reasons for introducing mt_rand.



Speed was not why mt_rand was introduced!

The rand function existed way before mt_rand, but it was deeply flawed. A PRNG must get some entropy, a number from which it generates a sequence of random numbers. If you print out a list of ten numbers that were generated by rand() like so:

for ($i=0;$i<10;++$i)
echo rand(), PHP_EOL;

The output can be used to work out what the rand seed was, and with it, you can predict the next random numbers. There are tools out there that do this, so google a bit and test it.

There's also an issue with rand relativily quickly showing patterns in its random numbers as demonstrated here. A problem mt_rand seems to solve a lot better, too.

mt_rand uses a better randomization algorithm (Mersenne Twist), which requires more random numbers to be known before the seed can be determined and is faster. This does not mean that mt_rand is, by definition, faster than rand is, this only means that the way the numbers are generated is faster, and appears to have no real impact on the function's performance, as other answers here have demonstrated.

Either way, have a look at the mt_srand and the srand docs. I'm sure they'll contain some more info

If mt_rand's algorithm translates in an increase in performance, then that's great for you, but it's a happy coincidence. TL;TR:

mt_rand was introduced to fix the problems that exist in rand!

How is PHP's mt_rand seeded?

In PHP 5.4, if mt_rand is automatically seeded the first time it's used (PHP source). The seed value is a function of the current timestamp, the PHP process PID and a value produced by PHP's internal LCG. I didn't check the source for previous versions of PHP, but the documentation implies that this seeding algorithm has been in use starting from PHP 5.2.1.

The RNG algorithm behind mt_rand is the Mersenne Twister. It doesn't really make sense to talk about "how bad" it is, because it's clearly documented (not on the PHP docs page, unfortunately) that it is entirely unsuitable for cryptographic applications. If you want crypto-strength randomness, use a documented crypto-strength generator.

Update: You might also want to look at this question from crypto.SE.

PHP generating random numbers

Are you using rand()? Consider "generating a better random value".

Addendum; it's always good to see two sides of a coin.

PHP equivalent of javascript Math.random()

You could use a function that returns the value:

PHP

function random() {
return (float)rand()/(float)getrandmax();
}

// Generates
// 0.85552537614063
// 0.23554185613575
// 0.025269325846126
// 0.016418958098086


JavaScript

var random = function () {
return Math.random();
};

// Generates
// 0.6855146484449506
// 0.910828611580655
// 0.46277225855737925
// 0.6367355801630765

@elclanrs solution is easier and doesn't need the cast in return.



Update

There's a good question about the difference between PHP mt_rand() and rand() here:

What's the disadvantage of mt_rand?

If PHP's mt_rand() uses a faster algorithm than rand(), why not just change rand() to use the newer implementation?

Mainly because that's the PHP way. Just like they added mysql_real_escape_string instead of replacing mysql_escape_string with it.

However, it might also be related to the disadvantages the mersenne-twister algorithm has (I have no clue if they are also present in the rand() algorithm though):

The algorithm in its native form is not suitable for cryptography (unlike Blum Blum Shub). Observing a sufficient number of iterates (624 in the case of MT19937, since this figure is the size of the state vector from which future iterates are produced) allows one to predict all future iterates. A pair of cryptographic stream ciphers based on output from Mersenne twister has been proposed by Makoto Matsumoto et al. The authors claim speeds 1.5 to 2 times faster than Advanced Encryption Standard in counter mode. wikipedia

Another issue is that it can take a long time to turn a non-random initial state (notably the presence of many zeros) into output that passes randomness tests. A small lagged Fibonacci generator or linear congruential generator gets started much more quickly and usually is used to seed the Mersenne Twister with random initial values. wikipedia

using rand() for CSRF, can it be safe(r)?

It'll probably be good enough, as long as the generated tokens are user-specific and/or are expired relatively soon. However, if you're going to change it at all, you should change it to use a decent PRNG, which is available on most systems in the form of /dev/random and can be accessed using a number of ways:

mcrypt_create_iv($raw_salt_len, MCRYPT_DEV_URANDOM)
openssl_random_pseudo_bytes($raw_salt_len)
fopen('/dev/urandom', 'r') // then fread enough bytes from it

Simply bin2hex or base64_encode the return values of the above. Your rand (or better mt_rand) solution should only be a fallback in case none of the above are available.



Related Topics



Leave a reply



Submit