PHP - Replacing mcrypt_create_iv() with openssl_random_pseudo_bytes() - php

I need to up the security of our website, and is currently using the guide here: http://crackstation.net/hashing-security.htm, and also the generation of random passwords here: https://defuse.ca/generating-random-passwords.htm. I gather that both uses the function mcrypt_create_iv() for generating random bytes (or bits?), but for some reason, I encounter errors in installing php-mcrypt under CentOS 6. Fortunately, the first link said that openssl_random_pseudo_bytes() is a CSPRNG (and the PHP documentation and other sources also back that claim), and is available on the current server installation of PHP 5.4, so I have no choice but to use that at the moment. With these in mind, I would like to ask the following:
Does a direct code substitution suffice without affecting security? (That is, just replacing calls to mcrypt_create_iv() to openssl_random_pseudo_bytes() would do?)
About the constants mentioned in the code (http://crackstation.net/hashing-security.htm#properhashing), the guide says that "[m]ake sure your salt is at least as long as the hash function's output." Am I right in assuming that PBKDF2_SALT_BYTES and PBKDF2_HASH_BYTES are both set to 24 bytes since the output of the pbkdf2() function would be just 24 bytes, not 32 (for 256 bits) since the underlying algorithm used is sha256? (Yes, I am using key stretching too.) In a related note, is 24 bytes fine, or should be increased/decreased, and what effect would that have?
Advanced thanks for those who will answer.

I think the security will not be affected because both functions are just cryptographically secure pseudorandom number generators (NB: openssl_random_pseudo_bytes($len, true) and mcrypt_create_iv($len, MCRYPT_DEV_RANDOM)).
PBKDF2_SALT_BYTES is used only in the test function create_hash() and not in pbkdf2() itself. So you just need to implement your own salt generation function using those CSPRNGs.

Related

mcrypt 7.1 deprecated / 7.2+ removal

I have been following information on SO about generating random strings using mcrypt_create_iv() and have some example code over at 3v4l.org.
After reading the PHPWiki all mycrypt_* functions will show a depreceated warning but it does not specify what will be used in 7.2/8.0 that replaces these functions.
Since it states it will be removed in 7.2/8.0, what will be used to replace IV's?
I do not use this function in my software for encryption and decryption. I only use it for safe/retrievel of a unique string.
random_bytes()
Although I imagine each PHP cryptography extension would provide its own version of a function to generate random data, like they currently do:
ext/openssl provides openssl_random_pseudo_bytes() (but random_bytes() is better than it)
libsodium-php provides \Sodium\randombytes_buf()
That is simply because any cryptography API would be incomplete without access to a CSPRNG. In fact, mcrypt_create_iv() was added by the PHP developers for that same reason, while it is otherwise not part of libmcrypt.
I only use it for safe/retrievel of a unique string.
"Random" doesn't mean "unique". The former needs to be unpredictable, while the latter has to be unrepeatable.
It is true that the chances of a collision decrease exponentially with each random byte you add to a string, but uniqueness is never 100% guaranteed.

Making mt_rand() as secure as possible

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 :-)
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:
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().
When reading from /dev/urandom, cache your file descriptors to reduce the overhead of each function invocation.
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);).
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.
Don't use the modulo operator to reduce a random integer to a range. This will result in a biased probability distribution:
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.
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:
I forgot your password: Randomness attacks against PHP applications
Cracking PHP's lcg_value()
phpwn: Attack on PHP sessions and random numbers
To my knowledge, the best option for secure (pseudo) random number generation in PhP applications is to use openssl_random_pseudo_bytes.
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.

PHP: openssl_random_pseudo_bytes() and Crypto Security vs Extreme Randomness

Doing some research into openssl_random_pseudo_bytes() in PHP and I noticed that in the implementation of the of the openssl_random_pseudo_bytes()function in PHP's source. OpenSSL's RAND_pseudo_bytes function is used to generate the return value as opposed to RAND_bytes also available in OpenSSL.
OpenSSL's documentation of these two functions are as follows:
RAND_pseudo_bytes() puts num pseudo-random bytes into buf.
Pseudo-random byte sequences generated by RAND_pseudo_bytes() will be
unique if they are of sufficient length, but are not necessarily
unpredictable. They can be used for non-cryptographic purposes and for
certain purposes in cryptographic protocols, but usually not for key
generation etc.
RAND_bytes() puts num cryptographically strong pseudo-random bytes
into buf. An error occurs if the PRNG has not been seeded with enough
randomness to ensure an unpredictable byte sequence.
I guess my question is why wasn't RAND_bytes used or why isn't there also a openssl_rand_bytes() function in PHP if it is, according to OpenSSL, more random.
Just curious. Was it a speed concern? Not reliable enough? Or was the PRNG the issue (ie: to hard to implement when the pseudo works fine for most purposes) ?
Thanks
The choice was probably made based on practicality rather than the soundness of the crypto. If RAND_bytes() is used, the function may fail due to insufficient randomness being available. The author of the PHP code no doubt wanted to avoid the PHP function failing.
I notice though that the openssl_random_pseudo_bytes() function does have an optional crypto_strong parameter, which lets the caller know whether the returned bytes really were cryptographically strong, in the opinion of OpenSSL.
As an aside, it is possible to configure OpenSSL with external engines and some of them (such as CHIL) use a hardware-based random source for both RAND_pseudo_bytes() and RAND_bytes() if that's what you need.
Also, on Windows the PHP code is using CryptGenRandom.

How is PHP's mt_rand seeded?

I know PHP's mt_rand() should not be used for security purposes as its results are not cryptographically strong. Yet a lot of PHP code does just that, or uses it as a fallback if better sources of randomness are not available.
So how bad is it? What sources of randomness does mt_rand use for seeding? And are there other security problems with mt_rand for cryptographic applications?
In PHP 5.4, if mt_rand is automatically seeded the first time it's used (PHP source). The seed value is a function of the current timestamp, the PHP process PID and a value produced by PHP's internal LCG. I didn't check the source for previous versions of PHP, but the documentation implies that this seeding algorithm has been in use starting from PHP 5.2.1.
The RNG algorithm behind mt_rand is the Mersenne Twister. It doesn't really make sense to talk about "how bad" it is, because it's clearly documented (not on the PHP docs page, unfortunately) that it is entirely unsuitable for cryptographic applications. If you want crypto-strength randomness, use a documented crypto-strength generator.
Update: You might also want to look at this question from crypto.SE.

Accessing CryptGenRandom from PHP

I'm having trouble finding a way to access secure random numbers on Windows from PHP in a way that will stand a good chance of working on any given installation.
A lot of windows installs come without openssl or with it disabled, so openssl_random_pseudo_bytes() often doesn't work.
Same kind of problem with mcrypt_create_iv() when mycrypt extension isn't available.
session_id() (with session.entropy_length set to a resonable number) is another route but safe mode sometimes disables the functions needed to regenerate and get the session ID.
new COM('CAPICOM.Utilities.1')->GetRandom() is kinda obsolete (Win32 only) and the dll isn't reliably available.
new DOTNET('mscorlib', 'System.Security.Cryptography.RNGCryptoServiceProvider') with GetBytes() only works if .NET is installed and is working compatibly with PHP.
What's a better solution?
I have actually implemented an RFC4086 compliant Random Number Generator in PHP. You can see the work at the GitHub Project for PHP-CryptLib.
Basically, you specify the "strength" of random number/string that you require. If you need cryptographically secure numbers, select High (but note, this can be a blocking operation. If you just need good strength numbers/strings, I'd suggest the Medium setting. If you're just implementing a game with no security implications, choose Low.
So here's how to use it:
require_once dirname(dirname(__DIR__)) . '/lib/CryptLib/bootstrap.php';
$factory = new \CryptLib\Random\Factory;
$generator = $factory->getLowStrengthGenerator();
$number = $generator->generate(8);
That will generate an 8 byte string (chr 0-255) at low strength (you could do ->getHighStrengthGenerator for a high one). The difference is how it sets up the generator (which mixer it uses, and which sources it uses).
To generate an integer between 1 and 10 (inclusive):
$int = $generator->generateInt(1, 10);
Note that RFC4086 specifies a way to generate randomness from multiple sources (so that if any one source is compromised, the final result will not be significantly weakened). So when you select the High strength source, it uses the same sources as the Medium one, but with one or more added from the high strength category.
Note that according to the RFC, the strength of the output is at least as good as the strongest non-compromised source being used. So this really will be the best quality RNG for an application that you can get (the design)... If you wanted to add a hardware TRNG, you could and still use the mechanism (the sources are plugable, so it should be able to add anything without harming the output)...

Categories