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)...
Related
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.
Does anyone know how to generate a long (e.g. 280 characters) random string in PHP without having to use a for loop that will loop through characters 280 times? I need it in order to create a custom session ID.
The PHPSESSID is not secure enough in my opinion being too short and not too random. I know Facebook and Twitter, use long session IDs (150, 550 chars respectively).
There could be an option to use MD5 strings or Bcrypt encryption of different string such as PHPSESSID, host, User-Agent etc. but I'm not sure this is the right way of doing it.
If you're asking a question like that, it probably means you don't know anything about cryptography or security. Trying to generate a "long random string" because, as you say, "The PHPSESSID is not secure enough" will probably lead you to a custom and insecure implementation.
Generating a random string is IMPOSSIBLE, at least not with your current hardware: you may approximate a fair pseudorandom generator but that is only useful for educational purposes.
PHP's Session ID generation algorithm is fairly efficient; if you think it is not secure enough, then you'll likely waste time making it better. You may probably want to use a different authentication mechanism if you are looking at maximum security (using a client certificate for example).
If websites such as Twitter, Facebook, or another site with similar traffic use longer session IDs, it may be not because it is more secure (well in a way), but rather because it avoids conflicts.
Finally, if you want a longer session ID without trying to write your own algorithm, you should use the following PHP configuration directive:
session.hash_function which can take any hash algorithm known by PHP.
You may also want to use session.bits_per_characters to shorten or lengthen the string. Note that if you do this, the string may be longer or shorter, but the data remains the same -- only represented differently (base 16, base 32, etc.)
Additional info:
You may also increase the entropy by using a custom source (file) and setting the length of the seed:
ini_set("session.hash_function", "sha512");
ini_set("session.bits_per_charater", 4); // 4 means hex
ini_set("session.entropy_file", "/dev/urandom");
ini_set("session.entropy_length", "512");
Can it be binary hexadecimal?
bin2hex(mcrypt_create_iv(140, MCRYPT_DEV_URANDOM));
Or if you have access to hash_pbkdf2():
hash_pbkdf2('sha256', PHPSESSID, mt_rand(), 1, 280, false);
You'll probably find that not all the bits in a "long" session id are purely random - they might provide additional information to the application to enable rapid extraction of key information.
PHP session ids by default aren't too bad, and can be improved by providing some extra entropy (e.g. from /dev/urandom)
Look at php_session_create_id in ext/session/session.c in the php source
It goes like this:
get time of day
get remote ip address
build a string with the seconds and microseconds from the current time, along with the IP address
feed that into configured session hash function (either MD5 or SHA1)
if configured, feed some additional randomness from an entropy file
generate final hash value
So getting a duplicate or predicting a session id is pretty difficult.
Make yourself a long string and shuffle it.
$string = 'asdfhdfiadfshsdfDSGFADSFDSFDSFER524353452345';
$new_string = shuffle($string);
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.
In order to guarantee honesty of a random number generator, the idea is that users can, if they wish, verify that the number is, in fact, generated from public sources of entropy. This enables the system to ensure it's users that the random number could not have been selected by the server.
$entropy = "what_do_you_think";
$md5 = md5($entropy);
/*take the first 10 hex characters of the md5 hash*/
$hex = substr($md5, 0, 9);
/*convert the hex to decimal*/
$dec = hexdec($hex);
/*use this decimal as a seed*/
srand($dec);
/*pick a random number between 0 and 9, ultimately seeded by the entropy*/
$rand = rand(0,9);
My question is: What are some good public sources of entropy (preferably immutable and chaotic), and absolutely referencable, that could be concatenated together in a string and fed into md5? Some ideas are specific stock prices, temperature (from an honest source), the hashes contained in the bitcoin block-chain...
Check out xkcd's geohashing algorithm. I think it is pretty much what you are looking for.
http://wiki.xkcd.com/geohashing/Implementations
The geohashing algorithm uses the DOW Jones as a source of entropy. This page discusses ways to get the Dow's opening price via the web.
http://wiki.xkcd.com/geohashing/Dow_Jones_Industrial_Average
But I think the best source of public, immutable, and verifiable entropy can be found in the BitCoin transaction database. It is widely distributed and continuously verified and has a defined protocol.
Get it from a physics department.
http://qrng.physik.hu-berlin.de/
http://qrng.physik.hu-berlin.de/download
or just
http://www.random.org/bytes/
that users can, if they wish, verify that the number is, in fact, generated from public sources of entropy
How do they do that?
Do you give them realtime access to the system's memory to ensure that the assembly of the program running that collects entropy is correct and not malicious?
The security value of using physical entropy is that it's unpredictable, i.e. unknown to anyone but the acquirer. What on earth would be the point of using entropy that could be available to anyone? May as well open up your printout of Pi to a million places and pick a starting point.
Quite apart from that, there is in principle no way to determine whether the random numbers a server gives you were in fact derived from the sources of entropy it apparently uses.
In my application, I require a function to generate (unpredictably) random values that differ each time when called such as inside a fast loop.
On Linux platforms which is the platform I will release my script (of which shall be run under SSL in PHP) I will combine possibly multiple facilities to ensure a seed or hash is completely random, by querying /dev/random, possibly combined with OpenSSL's facilities and including system-specific values such as script last modified and creation time.
I am using these specific values, as even if person A had the script and knows the methods, they would not be able to guess the (/dev/random contents, memory usage at the moment, modification time likely, etc) and will not realistically be able to reduce the security of user B running the same script.
On the Windows platform which unfortunately I must develop on for the moment (I still test on Linux, but less often) I require random values of which I described above, just to provided at least limited protection from predicting the seeds or keys.
I had tried as a first attempt using memory_get_usage() (with or without available true parameter for 'true' memory usage for PHP) and it seems that the values remain very static even when each iteration performs a fair amount of memory heavy computation.
Would it maybe be wise to use this (somewhat dynamic) memory usage as a seed, for a PRNG to generate more (quickly) random numbers? Or would the fact that memory is such a limited range they could just create 2^xx seeds and roughly guess it.. I am starting to blur the line of what is realistically random, if it is even possible to guess my operations even if they are 'not' really that random.
The standard equivalent of the /dev/random (or the generally recommended /dev/urandom) Unix device on Windows is the CryptGenRandom function from CryptoAPI.
In PHP, you should be able to use mcrypt_create_iv() with MCRYPT_DEV_URANDOM, which uses /dev/urandom on Unix and (apparently) CryptGenRandom on Windows.
A Mersenne Twister (what mt_rand uses) is a good algorithm for non-security purposes but it shouldn't be used for security. Wikipedia: Mersenne Twister:
"The algorithm in its native form is not suitable for cryptography... Observing a sufficient number of iterates (624 in the case of MT19937) allows one to predict all future iterates."
Instead it's just as simple to just take the output of a counter, concatenate (or XOR) it with some salt, and hash it with a cryptographically secure hash algorithm like SHA-2. If no one knows your salt, it will be absolutely secure. The salt is then equivalent to Mersenne's seed.
I'm no expert on where to get good random salt on Windows, but you can always concatenate (or XOR) things like system time, memory usage, etc, and hash that with SHA-2. You can even reach outside to a place like Random.org for some true random numbers (if you don't call it too often). The best part about combining sources of randomness with SHA-2 is that every additional source can only add randomness, not subtract it.
Why not just use something like?
mt_rand({min}, {max});
More info here: http://php.net/manual/en/function.mt-rand.php