Take the 2-minute tour ×
Information Security Stack Exchange is a question and answer site for Information security professionals. It's 100% free, no registration required.

Reading about how insecure PHP's rand function is, I was wondering if it helps to combine random functions. The suggested function there, openssl_random_pseudo_bytes, can also be cryptographically insecure (see the second argument). Combining the output from this and rand() and perhaps also mt_rand() might lengthen the period:

function more_randomness() {
    $n = 0;
    $n += rand(0, 255);
    $n += mt_rand(0, 255);
    $n += ord(openssl_random_pseudo_bytes(1));
    return round($n / 3);
}

If this is completely stupid, just tell me. Also tell me how else to generate cryptographically secure random numbers when this returns false:

function has_bad_prng() {
    openssl_random_pseudo_bytes(1, $is_bad);
    return $is_bad;
}
share|improve this question

3 Answers 3

up vote 7 down vote accepted

A PRNG can be insecure for several reasons, but one of them is using as seed some data which can have only a limited number of distinct values. For instance, if you use the current time as seed, then that value is known to the attacker, or at least potentially known with a not-so-hard effort: even if you have an internal clock with microsecond precision, and the attacker knows that clock only down to an accuracy of one second or so, then this amounts to only a million or so of possible seed values.

By enumerating the possible seed values, the attacker can recompute the same possible streams than you. You can add together hundreds of PRNG with any algorithms you want; if they all use the current time as seed, this will not save you.

"Adding" together several PRNG might help if the PRNG are all properly seeded (with distinct seed sources) and some of the PRNG are cryptographically weak. But there are subtle details; it is not easy to do right, and usually not worth the effort.

If openssl_random_pseudo_bytes() returns "insecure" then there is very little you can do to salvage it. Also, this does not happen in practice, unless your system is somehow damaged. Any decent server, be it Linux, Windows, MacOS, FreeBSD... maintains a cryptographically strong PRNG seeded with hardware events (that's /dev/urandom, CryptGenRandom()...) and that's what OpenSSL uses. If this does not work, then things have gone sour quite deeply in your system, and it is time to panic (i.e. raise exceptions and alert the sysadmin).

share|improve this answer

If the reason the functions are insecure is because you can exhaust the seed space after seeing a very small number of values, then this approach might give the sum of the number of seed bits of security, assuming the seeds were perfectly random.

If the problem is that they have a short period, if the functions have pairwise co-prime periods then the period of the composite function is the multiple of the three periods, otherwise it is the lowest common multiple.

All in all, the increase in security is probably marginal, given that the periods are likely to be powers of two, and that a combination of exhausting the seed space of one and the period of the other would be an effective tactic. In theory you could have an exponential increase in security, but the chances are that a combination attack would be quickly found. For example the seed of one might not be independent of the seeds of the others if they are time based and the calls are sequential.

To answer the second part of your question, I saw this link on SO to some code that might do what you want.

share|improve this answer

Technically, you improve the security a little, but to no useful end.

Random numbers usually come from a pseudo-random number generator (PRNG) that is itself seeded with a random value. The purpose of the PRNG is to stretch the original seed to produce a very long output. Random numbers are usually invoked for two reasons:

  • To create random-looking data for somewhat casual use. Perhaps to give characters in a video game a random-looking strategy, maybe to run a randomized algorithm, etc. The general requirement is that the random values be well distributed and pass basic statistical tests, although the exact usage dictates exactly what the requirements are.

  • To provide cryptographic security. Perhaps to generate a secret key, to start the search for a random prime number, etc. The requirement is that the random values be very well distributed, pass many advanced statistical tests, have a very large seed space, and yield no information about other values in the output or anything about the seed of the PRNG.

PRNGs are usually designed for one of those requirements, and they are nothing alike. The PRNGs for casual use tend to be very simple, extremely fast, and meet none of the requirements of a cryptographic PRNG. It's very difficult turn a simple PRNG into a cryptographic PRNG; in order to do so there would likely be very little resembling the original PRNG left in the final secure PRNG scheme.

Here are some the problems with combining the weak PRNGs:

  • Period length. When you combine two PRNGs linearly (like in the OP) you will at best combine their seed lengths. Simple PRNGs generally have 32 bits or less of seed length, which is cryptographically very insecure. You should generally have at least 128 bits of seed space.

  • Seed protection. Combining the PRNG outputs won't necessarily prevent the seed from being reverse-engineered from the PRNG output. If an attacker can derive part of the seed, they can reduce their work load drastically. Deriving the seed from the output of a simple PRNG is sometimes trivial in proper circumstances.

  • Output bias. Linearly combining the PRNG outputs won't necessarily hide their flawed outputs. It should definitely improve the bias, but it is unlikely that they will be as good as a cryptographic PRNG. (Practically, this is probably less of a concern, but still worth mentioning.)

And that's assume you seed the PRNG properly. You still need to do that seeding manually as whatever default seeding the simple PRNGs use is probably based on something very low quality, like a timestamp.

The simple summery is that combining weak PRNGs provides better security, but not the level of security you should expect from a cryptographically secure PRNG. The solution is to get a real cryptographic PRNG and seed it with random data from a good source, like /dev/random or CryptGenRandom(). Or you could just use the output of those directly since they are technically PRNGs themselves seeded by other random data.

If openssl_random_pseudo_bytes sets the flag indicating that it did not generate secure random bytes, that would seem like there is something wrong with the environment because the manuals suggest that it should be expected to return true under normal conditions. Check to see what version of PHP / OpenSSL is installed and whether you can update it. If you can't get it to work, consider just getting your random data directly from /dev/random or CryptGenRandom().

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.