0

I wrote a function in PHP to generate a random password (only 0-9a-zA-Z) for my app. The resulting password must be cryptography secure, and as random as possible. I.E. the passwords are sensitive.

The big trick I do is shuffle $possible characters everytime, so even if mt_rand() is not truely random, it should not be predictable.

Any recommended changes or security issues in my function? Is using openssl_random_pseudo_bytes() instead of mt_rand() really going to make the algorithm stronger and more secure?

    public function generate_random($length = 15) {
        $random = "";
        $possible = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
        $possible = str_shuffle($possible);

        $maxlength = strlen($possible);

        if ($length > $maxlength) {
            $length = $maxlength;
        }

        $i = 0;

        while ($i < $length) {
            $random .= substr($possible, mt_rand(0, $maxlength-1), 1);
            $i++;
        }

        return $random;
    }

Thanks.

Justin
  • 42,716
  • 77
  • 201
  • 296

4 Answers4

2

To generate something really random, you have to use the random source of the operating system. After reading from this source, you need to encode the bytes to an alphabet of your choice.

An easy conversion is base64 encoding, but this will include '+' and '/' characters. To only get characters from the alphabet and digits, you need a base62 encoding, or you can simply replace those characters with other characters.

/**
 * Generates a random string of a given length, using the random source of
 * the operating system. The string contains only characters of this
 * alphabet: +/0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
 * @param int $length Number of characters the string should have.
 * @return string A random base64 encoded string.
 */
function generateRandomBase64String($length)
{
  if (!defined('MCRYPT_DEV_URANDOM')) die('The MCRYPT_DEV_URANDOM source is required (PHP 5.3).');

  // Generate random bytes, using the operating system's random source.
  // Since PHP 5.3 this also uses the random source on a Windows server.
  // Unlike /dev/random, the /dev/urandom does not block the server, if
  // there is not enough entropy available.
  $binaryLength = (int)($length * 3 / 4 + 1);
  $randomBinaryString = mcrypt_create_iv($binaryLength, MCRYPT_DEV_URANDOM);
  $randomBase64String = base64_encode($randomBinaryString);
  return substr($randomBase64String, 0, $length);
}

The code is part of this class, have a look at the function generateRandomBase62String() for a complete example.

martinstoeckli
  • 23,430
  • 6
  • 56
  • 87
1

Adding pseudo-randomness to a pseudo-random string won't increase the entropy at all. The only way is to use a better random number generator.

Possible duplicate: Secure random number generation in PHP

Community
  • 1
  • 1
ntoskrnl
  • 5,714
  • 2
  • 27
  • 31
1

If by cryptographically secure, you mean you intend to use the password as a key somewhere, it is important to realize that your space isn't nearly large enough. 15 characters with 62 possibilities each is less than 90 bits, which is about as strong as RSA-1024, and is considered unsafe today.

You should, however, not be doing such a thing in the first place. If you do require a human-readable string that maps to something that can be used as a cryptographic key, use something like PBKDF2.

Lastly, shuffling the string does not increase effective randomness. As long as you do not use it directly as a key, your function is fine - remember to first check the output against a dictionary of common passwords
(like a password list from a password cracker) and reject those.

Community
  • 1
  • 1
santosh.ankr
  • 671
  • 4
  • 12
0

This is not an answer of your question but it seems feasible to use this function to generate random random password containing only (0-9a-z,A-Z)

$password = base64_encode(openssl_random_pseudo_bytes(20, $strong));
$newstr = preg_replace('/[^a-zA-Z0-9\']/', '', $password);
echo $newstr;
Rajeev Ranjan
  • 4,152
  • 3
  • 28
  • 41