5

Goal: Find the most cryptographically secure random string generator. Using Alphabetic, numeric and if possible special characters in the string.

I have been reading on here and other places, but I still hear so many different answers/opinions. Can people who are up to date and knowledgeable about security and cryptography chime in here.

The following functions will be used to generate a 8 character random password and also generate a 128 character random token.

Function 1:

/**
 * Used for generating a random string.
 *
 * @param int $_Length  The lengtyh of the random string.
 * @return string The random string.
 */
function gfRandomString($_Length) {
    $alphabet = "abcdefghijklmnopqrstuwxyzABCDEFGHIJKLMNOPQRSTUWXYZ0123456789";
    $pass = array(); //remember to declare $pass as an array
    $alphaLength = strlen($alphabet) - 1; //put the length -1 in cache
    for ($i = 0; $i < $_Length; $i++) {
        $n = rand(0, $alphaLength);
        $pass[] = $alphabet[$n];
    }
    return implode($pass); //turn the array into a string
}

Function 2:

The PHP.net docs say: crypto_strong: If passed into the function, this will hold a boolean value that determines if the algorithm used was "cryptographically strong", e.g., safe for usage with GPG, passwords, etc. TRUE if it did, otherwise FALSE.

So is that based on the the server? If I test it once, and it is able to generate a crypto_strong string, will it always be able to? Or would I need to check each time and create a loop until it generates a crypto_strong string.

/**
 * Used for generating a random string.
 *
 * @param int $_Length  The length of bits.
 * @return string The random string.
 */
function gfSecureString($_Length) {
    $Str = bin2hex(openssl_random_pseudo_bytes($_Length));
    return $Str; 
}

I welcome any suggestions to improve the cryptographic strength.

Peter O.
  • 32,158
  • 14
  • 82
  • 96
M H
  • 2,179
  • 25
  • 50
  • That question does not have an answer. – M H Jul 18 '15 at 16:00
  • haha that is one of the main questions I read. That question has a bunch of people arguing about it and the accepted answer uses the rand() function, which a bunch of people disagree with. The point of this question is to actually see which one is secure. REAL answers, not just ideas or opinions. – M H Jul 18 '15 at 16:16
  • 1
    You'll probably have to read through them a bit. (Hint: the `random_int` one). Or wait for the bounty marker. That the ✔ check mark is on the least useful answer there doesn't make it less of a duplicate. Let's not dilute the topic further with new questions each week. – mario Jul 18 '15 at 16:29
  • 1
    That question has NOTHING to do with being cryptographically secure. Hense the accepted answer is NOT secure. My question is going in a completely different direction. – M H Jul 18 '15 at 16:38
  • So your complaint is that it's *just the title* of the duplicate that fits no more? Or do you insist on a personal reexplaination why neither `rand` nor `bin2hex` from your two examples is cryptographically secure? – mario Jul 18 '15 at 17:16
  • I am looking for a Cryptographically secure string generating function, with my current functions as the starting point. The question you marked as duplicate is only asking for a random generator function. Which is why he marked the insecure function as the answer, it did what he asked. Now, that question is answered and my question is closed and I wont get real answers to my question about generating a cryptographically secure string function. – M H Jul 18 '15 at 17:21
  • Gosh. Don't be that oblivious. Nobody is forcing you to ignore anything but the accepted answer. *Dozens* of people have contributed there to explain how to generate a cryptographically secure string. Isn't that your question? Then why chose to ignore *everything* just because of the first answer? You're not actually looking for the answer, but an answer duplicated on your question, right? – mario Jul 18 '15 at 17:28
  • I read every one of those answers and since that question is not about being secure, there is not one with a voted consensus as the TRUE answer to create a cryptographically secure string, what a surprise. And yes, the fact that the question title is not correct makes it so that it will not receive answers by people who actually know this stuff. – M H Jul 18 '15 at 17:36
  • The fact that you do not have an answer posted on there and you have not even suggested one as an answer to my question, tells me that you probably don't know. Instead of being construstive, you just want to close people questions for incorrect reasons. But anyways, you've wasted enough of my time. Thanks for the great input sir. – M H Jul 18 '15 at 17:36
  • 1
    [See this answer](http://stackoverflow.com/a/31284266/2224584). – Scott Arciszewski Jul 18 '15 at 20:26

2 Answers2

8

So you want to securely generate random strings in PHP. Neither of the two functions in the question will give you what you want, but the rand() solution is the worst of the two. rand() is not secure, while bin2hex(openssl_random_pseudo_bytes()) limits your output character set.

Also, openssl_random_pseudo_bytes() might not be reliable under extreme conditions or exotic setups.

From what I understand, crypto_strong will only be set to false if RAND_pseudo_bytes() fails to return any data. If OpenSSL is not seeded when it's invoked, it will silently return weak (and possibly predictable) pseudorandom bytes. You have no way, from PHP, to determine if it's random either.

How to generate secure random strings today

If you want a solution that has received substantial review for PHP 5.x, use RandomLib.

$factory = new RandomLib\Factory;
$generator = $factory->getMediumStrengthGenerator();
$randomPassword = $generator->generateString(20, $alphabet);

Alternative solutions

If you'd rather not use RandomLib (even if, purely, because you want to have alternative options available), you can also use random_int() when PHP 7 comes out. If you can't wait until then, take a look at our random_compat project.

If you happen to be using the cryptography library, libsodium, you can generate random numbers like so:

/**
 * Depends on the PECL extension libsodium
 * 
 * @link https://stackoverflow.com/a/31498051/2224584
 * 
 * @param int $length How long should the string be?
 * @param string $alphabet Contains all of the allowed characters
 * 
 * @return string
 */
function sodium_random_str($length, $alphabet = 'abcdefghijklmnopqrstuvwxyz')
{
    $buf = '';
    $alphabetSize = strlen($alphabet);
    for ($i = 0; $i < $length; ++$i) {
        $buf .= $alphabet[\Sodium\randombytes_uniform($alphabetSize)];
    }
    return $buf;
}

See this answer for example code that uses random_int(). I'd rather not duplicate the effort of updating the code in the future, should the need ever arise.

Community
  • 1
  • 1
Scott Arciszewski
  • 33,610
  • 16
  • 89
  • 206
2

openssl_random_pseudo_bytes has a pretty large chance of being a cryptographically secure generator, while rand certainly isn't. However, it will only return binary data which you revert to hexadecimals. Hexadecimals are not enough to generate a password string. Neither function includes special characters as you seem to require.

So neither one of the code snippets fits your purpose.

Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
  • Yes, but changing it from bin2hex to something that generates passwords is not that easy. First you need to change it to create a value within a range (without losing randomness), and then you have to use that to generate a password securely. – Maarten Bodewes Jul 18 '15 at 16:07
  • Maarten: What are your thoughts on [random_compat](https://github.com/paragonie/random_compat)? :) – Scott Arciszewski Jul 18 '15 at 20:28
  • @ScottArciszewski It seems to be a well written API (well, function call really) that retrieves the random bytes from a number of sources, in order of preference. Currently Source Forge is offline, but I think it is weird that an unmaintained library (with a function that is meant for generating IV's) is considered the prime source. Codes has looked at it so I would certainly trust it to a big extend, but it feels too much like band aid to me. Then again, a PRNG written in PHP may be too slow and may have other security issues. In itself, it doesn't solve this question (no ranged number). – Maarten Bodewes Jul 19 '15 at 12:07
  • Sourceforge? I'm not sure what you're looking at. – Scott Arciszewski Jul 19 '15 at 18:21
  • Ok, that was the mcrypt library. Didn't see that function, I'll look again. – Maarten Bodewes Jul 19 '15 at 18:23
  • Oh, I just reread your comment and know what you mean now. https://github.com/paragonie/random_compat/blob/master/ERRATA.md This might help :) – Scott Arciszewski Jul 20 '15 at 01:09
  • Yep, it helps. **If** `mcrypt` is secure for the random functions then the choice does make sense. I haven't seen any security reviews of the `mcrypt` code though, but it should be relatively easy to only review the code required. – Maarten Bodewes Jul 20 '15 at 22:24