6

I came across an application which needs to generate a random token for each user. It is very important that each generated token is unique. Application code was as follows:

$token = md5(open_ssl_random_pseudo_bytes(64));

I don't know exactly how open_ssl_random_pseudo_bytes works or why the number "64" was chosen, nor how those bytes play together with MD5 hashing.

So how random is this token really? If the code is ran 1 billion times, will the tokens be unique?

3
  • If the code is ran 1 billion times, will the tokens be unique? For this something like 8 bytes would be more then enough, IF openssl_random_pseudo_bytes works correctly on your system.
    – Cthulhu
    Sep 25, 2015 at 11:24
  • No, you should not really rely on OpenSSL here #1 #2 #3. As soon as you’re on PHP 7.0+, there’s really no excuse anymore to use it. From that version on, always prefer the built-in random_bytes function.
    – caw
    Nov 22, 2019 at 18:27
  • There is no way to ensure uniqueness of random or pseudorandom numbers, by definition, no matter how they are obtained. If you are generating a unique identifier, ensure it is unique the way Microsoft GUIDs are unique: have one component be a location, another be a time. Or else maintain a hash table of all generated IDs so you can detect duplicates. Feb 5, 2021 at 20:56

4 Answers 4

8

open_ssl_random_pseudo_bytes is a cryptographically secure pseudo random number generator (CSPRNG).

In layman's terms, this means that it can generate an unpredictable, uniformly distributed sequence, that is suitable for key generation. The unpredictable property is important, because even if the full state of the random number generator is known, an attacker cannot recreate previously generated sequences, and it is unfeasible for an external observer to guess the state in order to predict future sequences.

This is in contrast to say a random number generator that takes its seed from the number of seconds past midnight and then generates predictable sequences based on this seed. In this case an attacker can simply set their own system to generate numbers using the same seed and can effectively guess token values in order to use them for their own nefarious purposes.

64 bytes will give you 512 bits of entropy. Since MD5 outputs hashes of 128 bits, there is no advantage of having the entropy generated being greater than 128. If the code is ran one billion times then there is a statistically probable chance of it always being unique because you have an output keyspace of 2128 (3.4 * 1038). The collision rate of MD5 is about 264 which is about 18 billion billion.

4
  • Let's say the system uses number that gives more than 128 bits (64 for example), how does it affect collision rate? Is there a greater or smaller probability then? Or it simply doesn't matter? Sep 25, 2015 at 11:33
  • For all practical purposes I'd say it doesn't matter - you're still getting a 128 bit hash output - the chances of an accidental clash (even with MD5) on input greater than 128 bits are improbable, even though the collision resistance property of MD5 is broken. The code above looks like it is only generating an MD5 so it has a fixed length string to work with. It could simply do this with the bin2hex function over 16 bytes in order to remove the need for hashing completely. Sep 25, 2015 at 13:00
  • To be exact, CSPRNG only guarantees that previous output cannot be reconstructed even if current state is known. If you know full state of any CSPRNG you can predict all future output unless that CSRPNG has additional entropy input. And in that case, the quality of output is strictly limited by quality of entropy input. Initial state / seeding or real entropy input are crusial for all CSRPNG implementations. "Random Number Generation Is Too Important to Be Left to Chance". Apr 25, 2016 at 6:29
  • @MikkoRantalainen: Thanks for pointing that out. Updated. May 20, 2016 at 7:43
7

You should be aware of PHP bug #70014 which was pretty recent and affects the reliability of openssl_random_pseudo_bytes().

I've been working on paragonie/random_compat, which backports random_bytes() from PHP 7 into PHP 5. One of the fallbacks it supports is openssl_random_pseudo_bytes(), but if it can read directly from /dev/urandom it will prefer that instead.

2
  • Note that Windows does not have the /dev/urandom mechanism. Feb 5, 2021 at 20:58
  • Creating a file in C:\dev\random can break some naive PHP code. :) Apr 19, 2021 at 3:18
1

There's another bug with openssl_random_pseudo_bytes (71915), which can result in duplicate values when you run it multiple times with the same process ID. Looks like it's fixed in 5.6.24.

1

It's better to use random_bytes than openssl_random_pseudo_bytes.

From the manual(available since php 7.x) :

random_bytes — Generates cryptographically secure pseudo-random bytes

Sample code (from the manual too):

 <?php

   $bytes = random_bytes(5);
   var_dump(bin2hex($bytes));

 ?>
5
  • Why is it better?
    – symcbean
    Mar 31, 2020 at 13:55
  • Cryptographically secure out of the box, cross platform and throws an exception when an appropriate source of randomness is not available
    – MarcoZen
    Mar 31, 2020 at 14:45
  • Are you saying that openssl_random_pseudo_bytes() doesn't do that? I was rather under the impression it did.
    – symcbean
    Mar 31, 2020 at 15:04
  • @symcbean It doesn't throw an exception. That second optional parameter of the openssl function looks a little worrying, as well. (Since it sounds like it's something that would need to be checked, but most people won't do this or know to do this.) The strongest benefit might be that it doesn't force a dependency on openssl. Some people use alternative TLS implementations. And for good reasons. Finally, the PHP function will even work on systems without HTTPS support. Apr 1, 2020 at 3:42
  • 1
    @symcbean And also, OpenSSL (I believe) still insists on using a custom userspace implementation of an entropy-collecting RNG. That's always a red flag. Exploitable bugs tend to surface in those types of implementations. And often... Apr 1, 2020 at 3:54

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .