2

I know that PHP used the system implementation for its rand() function, which is usually a weak LCG or LFSR implementation. Did this change? In case it still does, I am using Fedora 32.

PHP states in its documentation that rand() does not create cryptographically secure values.

I've written a small script, that creates a 400x400px PNG by randomly coloring pixels black or white:

<?php

$resolution = 400;

header("Content-type: image/png");
$im = imagecreatetruecolor($resolution, $resolution)
    or die("Cannot Initialize new GD image stream");

$white = imagecolorallocate($im, 255,255,255);

for ($y = 0; $y < $resolution; $y++) {
    for ($x = 0; $x < $resolution; $x++) {
        if (rand(0, 1)) {
            imagesetpixel($im, $x, $y, $white);
        }
    }
}
imagepng($im);
imagedestroy($im);

?>

I cannot see patterns anymore when using the function in PHP7.3 though. May there be patterns at a level I cannot display in my experiment?

rand():

enter image description here
random_int(),

for use where unbiased results are critical

according to PHP's docs

enter image description here

dmuensterer
  • 1,144
  • 4
  • 13
  • 3
    To point out succinctly: An RNG can provide perfect statistical randomness while providing no cryptographic security at all. – Xander Jun 11 '20 at 15:11
  • The image doesn't need to reveal any pattern and even it shows a pattern than the rand() is a very bad random number generator. The only obvious pattern should occur when the width of the image equal to is period. – kelalaka Jun 11 '20 at 19:07

1 Answers1

10

If you can't see obvious patterns, that does not make it cryptographically secure! See also my answer to this question on our sibling site: What does it mean for a random number generator to be cryptographically secure?

At the risk of stating the obvious, rand() and mt_rand() are NOT SECURE for security purposes. Do NOT use them for things like shuffling a deck of virtual cards in games where there is (virtual) money at stake, generating security tokens, or any such thing. Such random functions are meant for fast randomness where security does not matter. Common applications for insecure randomness are simulations and games (the game AI repeating itself in 1 out of every 2 billion games is fine, but a secret key repeating itself 1 in every 2 billion keys can be crackable in a reasonable amount of time).

By coincidence I was looking at this 2 hours ago as well, since I was curious if they had finally aliased rand() to use mt_rand() instead, since mt_rand has been both faster and better since its introduction many years ago, but this does not appear to be the case. Therefore:

  • To generate insecure, fast random: $n = mt_rand(0, 10); generates a number from 0 through 10 (inclusive) (docs)
  • To generate secure random data: $data = random_bytes(32); generates 32 bytes of random data (docs)
  • To generate a secure random number: $n = random_int(0, 10); generates a number from 0 through 10 (inclusive) (docs)
    Note that this only goes up to PHP_INT_MAX, which is at most approximately 264, so too small as a security key by itself. Use random_bytes() for generating secret keys, security tokens, etc. This function may aid in generating an unpredictable number, like for shuffling a deck of cards, where you may need a random number ≤52 instead of random bytes.
Luc
  • 31,973
  • 8
  • 71
  • 135
  • 1
    don't use `random_int(lower,upper)` to get randomness for cryptographic applications - on all relevant platforms, the underlying randomness is always defined on powers of two (byte-wise, in fact, usually), and doing a modulo on doesn't yield a uniform distribution. The `random_int` function is hence broken by design (if you recognize that as a PHP design pattern: not surprisingly.).You want `random_bytes`, or an `openssl_` function. – Marcus Müller Jun 11 '20 at 14:30
  • 1
    @MarcusMüller Wait what? Can you provide a reference for that, like source code or a bug ticket or anything? Because that sounds like a super beginner's mistake to do modulo on raw bytes (see the first link in my post, I even gave that as example). – Luc Jun 11 '20 at 16:53
  • 1
    Not that I don't believe you per se, but it would be good to get that fixed so a reference would be helpful to pinpoint the issue. – Luc Jun 11 '20 at 17:47
  • you're right, just modulo would be dangerous, and I'm relieved to say the PHP code is not as I remembered it. They simply draw random numbers, then restrict the range to be a multiple of the range, and then do the modulo operation: https://github.com/php/php-src/blob/master/ext/standard/random.c#L257 – Marcus Müller Jun 11 '20 at 17:53
  • So, the only bad thing here is the potential for inadvertedly drawing more random bytes than you've meant to, you're right. (the first version of that code isn't great; it's using `(long) pow(2.0, (double) bits)` to get a power of two; it's relying on `pow(2.0, something)` being exactly the actual power of two, and I'm not sure that's guaranteed. Anyway, I would indeed expect it to work on any relevant machine. (but one wonders why someone implementing a C cryptographically secure random number generator hasn't heard of `1ULL << bits`) – Marcus Müller Jun 11 '20 at 18:02
  • @Luc is this what you are missing? [Why do people say there is modulo bias when using a random number generator?](https://stackoverflow.com/q/10984974/1820553) – kelalaka Jun 11 '20 at 19:10
  • @kelalaka I wasn't aware I'm missing something? I refer to literally that page through the explanation in the second sentence of my answer. – Luc Jun 11 '20 at 22:35
  • 1
    @MarcusMüller Thanks for getting back on that! Overdrawing randomness (within bounds) is not really a bug to me since I don't subscribe to the entropy disappearance theory ("entropy_avail"), so I'm happy to hear the answer didn't include insecure advice after all :) – Luc Jun 11 '20 at 22:37