The order is:
libsodium if available
fread() /dev/urandom if available
mcrypt_create_iv($bytes, MCRYPT_DEV_URANDOM)
COM('CAPICOM.Utilities.1')->GetRandom()
openssl_random_pseudo_bytes()
If libsodium is available, we get random data from it. This is the preferred method on all OSes, but libsodium is not very widely installed, so other fallbacks are available.
Next, we read /dev/urandom
(if it exists). This is the preferred file to read
for random data for cryptographic purposes for BSD and Linux. This step
is skipped on Windows, because someone could create a C:\dev\urandom
file and PHP would helpfully (but insecurely) return bytes from it.
Despite strongly urging people not to use mcrypt in their projects
(because libmcrypt is abandonware and the API puts too much responsibility on the
implementor) we prioritize mcrypt_create_iv()
with MCRYPT_DEV_URANDOM
above
the remaining implementations.
The reason is simple: mcrypt_create_iv()
is part of PHP's ext/mcrypt
code,
and is not part libmcrypt
. It actually does the right thing:
/dev/urandom
which
(unlike /dev/random
) is the sane and correct thing to do.CryptGenRandom
, which is an exclusively Windows
way to get random bytes.If we're on Windows and don't have access to mcrypt
, we use CAPICOM.Utilities.1
.
Finally, we use openssl_random_pseudo_bytes()
as a last resort, due to
PHP bug #70014. Internally, this
function calls RAND_pseudo_bytes()
, which has been deprecated
by the OpenSSL team. Furthermore, it might silently return weak random data
if it is called before OpenSSL's userspace CSPRNG is seeded. Also,
you want the OS CSPRNG, not a userspace CSPRNG.