6

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 :-)

Semger
  • 253
  • 1
  • 4
  • 12
  • 4
    Don't try polish a turd, use a secure rng instead of `mt_rand`. – CodesInChaos Jul 12 '15 at 09:06
  • 1
    Lol, "Don't try to polish a turd". In addition, thought it was worth mentioning [an example of using a poor RNG for a gambling application](https://jonasnick.github.io/blog/2015/07/08/exploiting-csgojackpots-weak-rng/). Sorry, there is no magic pixie dust that you can sprinkle on a RNG to make it secure for cryptography. Don't switch to [lcg_value](http://www.crypto-world.com/lcg_value.html) either. I recommend using [openssl_random_pseudo_bytes](http://php.net/manual/en/function.openssl-random-pseudo-bytes.php). – TheGreatContini Jul 13 '15 at 01:42
  • I really hope that was sarcasm – Scott Arciszewski Jul 14 '15 at 17:51

3 Answers3

12

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.
Scott Arciszewski
  • 33,610
  • 16
  • 89
  • 206
  • 1
    Coming from a cryptographic background, the following sentence you wrote rings alarms in my head: "As of this writing, it has not been cleared for a stable release yet, but it has passed the scrutiny of all the security experts I've shown it to." How about telling us who these so called experts are. – TheGreatContini Jul 13 '15 at 02:18
  • 2
    Certainly. CodesInChaos (one of the BLAKE2 authors), SammyK and Leigh (two of the authors involved in the PHP 7 CSPRNG feature), Taylor Hornby (Defuse Security), [Thomas Ptacek](https://twitter.com/tqbf/status/618609004600668160), Padraic Brady, Chris Cornutt, and sarnold in ##crypto. I've also shown it to Anthony Ferrara (ircmaxell) but I don't know how deeply he has examined it. – Scott Arciszewski Jul 13 '15 at 02:22
  • 2
    The actual list of reviewers and contributors is much longer. See https://github.com/paragonie/random_compat/issues/11 and the README for more information. – Scott Arciszewski Jul 13 '15 at 02:24
  • 1
    @TheGreatContini If you're not satisfied, please feel free to look at the code yourself and let me know if there's anything the library can do better. :) – Scott Arciszewski Jul 13 '15 at 02:36
  • @ScottArciszewski I thank you for your response, it was very detailed and you obviously took time to write it. The numbers I am generating, mt_rand() is used once per script. Apparently, it uses a random seed, so if I use it once per execution, does this mean a random seed will be used every time? Thanks in advance. If not, I will use that random bytes function – Semger Jul 14 '15 at 10:32
  • Even though it's seeded by PHP, [it can still be cracked](http://www.openwall.com/php_mt_seed/). – Scott Arciszewski Jul 14 '15 at 12:45
  • I'll see if I can squeeze in time on the weekend to have a look, but depends upon a number of other priorities. Will reply if I had time, otherwise it will delay until later. Thanks. – TheGreatContini Jul 14 '15 at 22:48
  • @TheGreatContini That's fine. By the way, thanks for asking for specific experts. :) – Scott Arciszewski Jul 15 '15 at 01:47
  • "In order of preference," I'd put `openssl_random_pseudo_bytes()` as first or second choice. Always worked for me. Lots of people I know run php code on Windows, /dev/* would fail then. To be fair, I didn't know about `mcrypt_create_iv`, that one seems good as well. Still, why is openssl *after* `/dev/*`? Or is it just your opinion? – Luc Jul 24 '15 at 19:12
  • 1
    "I'd put openssl_random_pseudo_bytes() as first or second choice." https://github.com/paragonie/random_compat/blob/master/ERRATA.md – Scott Arciszewski Jul 24 '15 at 19:19
1

mt_rand by its very name is the Mersenne Twister, a non secure random number generator. Furthermore it is often just seeded with a specific time in ms, something that an attacker can simply guess or aim for.

You cannot make the Mersenne Twister secure. So if anywhere possible you should use a secure random number generator seeded by an entropy source. This entropy source is usually obtained from the operating system. An OpenSSL based one should be preferred.

There is absolutely no reason why you would be stuck with MT. PRNG's are just algorithms. There are plenty of libraries that contain secure PRNG's.

Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
  • 1
    "You can code them yourself" - I'd say that that should come with a big warning. Writing a proper PRNG is *hard*, and you'll likely screw it up. – Sven Slootweg Jul 13 '15 at 03:02
  • @SvenSlootweg Maarten is one of the few people I'd expect to be able to write one without screwing it up, but nonetheless I agree that this is bad advice for the general public. – Scott Arciszewski Jul 13 '15 at 03:06
  • Yeah - if that was unclear, I was using 'you' in the general sense of advice to the public, not in the personal sense towards Maarten :) – Sven Slootweg Jul 13 '15 at 03:48
  • 1
    @ScottArciszewski If you use an existing secure hash as underlying function then the implementation risk (of a PRNG) can be mitigated, but I agree that this might be a bit too risky as general advice. Amended answer. – Maarten Bodewes Jul 13 '15 at 07:11
1

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:

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

TheGreatContini
  • 6,429
  • 2
  • 27
  • 37
  • 1
    I'm not sure that I quite agree with your last sentence. [`openssl_random_pseudo_bytes()` might not be the best solution](https://github.com/paragonie/random_compat/issues/6#issuecomment-119564973)... – Scott Arciszewski Jul 13 '15 at 02:47