Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Making mt_rand() as secure as possible

I am writing a raffle program where people have some tickets, which are marked by natural numbers in the range of 1 to 100 inclusive.

I use mt_rand(1,100) to generate the number of the winning ticket, and then this is outputted to the site, so everyone can see it.

Now I did a little research and found out from the Merseene wiki article that:

Observing a sufficient number of iterations (624 in the case of MT19937, since this is the size of the state vector from which future iterations are produced) allows one to predict all future iterations.

Is the current version used by mt_rand() MT19937?

If so, what can I do to make my generated numbers more cryptographically secure?

Thanks in advance :-)

like image 260
Semger Avatar asked Jul 12 '15 07:07

Semger


2 Answers

The short answer:

If so, what can I do to make my generated numbers more cryptographically secure?

You can simply use a random number generator suited for this task instead of mt_rand().

When PHP 7 comes out, you can use random_int() in your projects when a cryptographically secure random number generator is needed.

"Okay, great, but PHP 7 isn't out yet. What do I do today?"

Well, you're in luck, you have two good options available to you.

Use RandomLib. OR

I've been working on backporting PHP 7's CSPRNG functions into PHP 5 projects. It lives on Github under paragonie/random_compat.

"I don't want to use a library; how do I safely roll my own?"

When it comes to cryptography, rolling your own implementation is usually a poor decision. "Not invented here," is usually a good thing. However, if you're dead set on writing your own PHP library to securely generate random integers or strings, there are a few things to keep in mind:

  1. Use a reliable source of randomness. In order of preference, reading from /dev/urandom should be your first choice, followed by mcrypt_create_iv() with MCRYPT_DEV_URANDOM, followed by reading from CAPICOM (Windows only), and lastly openssl_random_pseudo_bytes().
  2. When reading from /dev/urandom, cache your file descriptors to reduce the overhead of each function invocation.
  3. When reading from /dev/urandom, PHP will always buffer 8192 bytes of data (which, likely, you will not use). Be sure to turn read buffering off (i.e. stream_set_read_buffer($fileHandle, 0);).
  4. Avoid any functions or operations that can leak timing information. This means, generally, you want to use bitwise operators instead of math functions (e.g. log()) or anything involving floats.
  5. Don't use the modulo operator to reduce a random integer to a range. This will result in a biased probability distribution: Biased distribution proof-of-concept
  6. A good CSPRNG will not fallback to insecure results. Don't silently just use mt_rand() if no suitable CSPRNG is available; instead, throw an uncaught exception or issue a fatal error. Get the developer's attention immediately.
like image 100
Scott Arciszewski Avatar answered Sep 28 '22 08:09

Scott Arciszewski


Sorry, but Mersenne Twister was not designed to meet cryptographic requirements. No, you cannot and should not try to fix it, because usually when non-experts try to improve cryptographic functionality, they just end up making things worse.

Php has a long history of problems with its randomness for cryptographic purposes. I'll point out a few references for light reading:

  • I forgot your password: Randomness attacks against PHP applications
  • Cracking PHP's lcg_value()
  • phpwn: Attack on PHP sessions and random numbers

To my knowledge, the best option for secure (pseudo) random number generation in PhP applications is to use openssl_random_pseudo_bytes.

like image 44
TheGreatContini Avatar answered Sep 28 '22 08:09

TheGreatContini