What is the best way to generate a random key within PHP?

Viewed 69383

I'm looking to create a reusable function that will generate a random key with printable ACSII characters of chosen length (anywhere from 2 to 1000+). I'm thinking printable ASCII characters would be 33-126. They key does not need to be completely unique, just unique if generated at the exact same millisecond (so uniqid() won't work).

I'm thinking a combination of chr() and mt_rand() might work.

Is this the way to go, or is something else the best method?

Edit: uniqid() will also not work because it doesn't have a length parameter, it's just whatever PHP gives you.

My Idea: This is what I came up with:

function GenerateKey($length = 16) {
    $key = '';

    for($i = 0; $i < $length; $i ++) {
        $key .= chr(mt_rand(33, 126));
    }

    return $key;
}

Are there any problems with this?

Another Edit: Most of the other questions deal with password generation. I want a wider variety of characters and I don't care about 1 vs l. I want the maximum number of possible keys to be possible.

Note: the generated key does not necessarily have to be cryptographically secure.

8 Answers

Update (12/2015): For PHP 7.0, you should use random_int() instead of mt_rand as it provides "cryptographically secure values"

Personally, I like to use sha1(microtime(true).mt_rand(10000,90000)) but you are looking for more of a customizable approach, so try this function (which is a modification to your request of this answer):

function rand_char($length) {
  $random = '';
  for ($i = 0; $i < $length; $i++) {
    $random .= chr(mt_rand(33, 126));
  }
  return $random;
}

Still, this will probably be significantly slower than uniqid(), md5(), or sha1().

Edit: Looks like you got to it first, sorry. :D

Edit 2: I decided to do a nice little test on my Debian machine with PHP 5 and eAccelerator (excuse the long code):

function rand_char($length) {
  $random = '';
  for ($i = 0; $i < $length; $i++) {
    $random .= chr(mt_rand(33, 126));
  }
  return $random;
}

function rand_sha1($length) {
  $max = ceil($length / 40);
  $random = '';
  for ($i = 0; $i < $max; $i ++) {
    $random .= sha1(microtime(true).mt_rand(10000,90000));
  }
  return substr($random, 0, $length);
}

function rand_md5($length) {
  $max = ceil($length / 32);
  $random = '';
  for ($i = 0; $i < $max; $i ++) {
    $random .= md5(microtime(true).mt_rand(10000,90000));
  }
  return substr($random, 0, $length);
}

$a = microtime(true);
for ($x = 0; $x < 1000; $x++)
  $temp = rand_char(1000);

echo "Rand:\t".(microtime(true) - $a)."\n";

$a = microtime(true);
for ($x = 0; $x < 1000; $x++)
  $temp = rand_sha1(1000);

echo "SHA-1:\t".(microtime(true) - $a)."\n";

$a = microtime(true);
for ($x = 0; $x < 1000; $x++)
  $temp = rand_md5(1000);

echo "MD5:\t".(microtime(true) - $a)."\n";

Results:

Rand:   2.09621596336
SHA-1:  0.611464977264
MD5:    0.618473052979

So my suggestion, if you want speed (but not full charset), is to stick to MD5, SHA-1, or Uniqid (which I didn't test.. yet)

You can still use uniqid(), just do some additional processing to expand its value to the number of characters you need.

For example, to expand it to 32 characters, you could do

$id = md5(uniqid());

To expand it to 64 characters, just append the md5 of the md5, like so

$first = md5(uniqid());
$id = $first . md5($first);

Then, trucate as necessary, if you need less than some multiple of 32.

It's possible you could run into collisions, but it's pretty unlikely. If you're paranoid about that, just use the same idea, but chug uniqid() through a symmetric cipher like AES instead of hashing it.

Does that question would be of interest to you?

I'm not sure why uniqid() doesn't work for you and in what case you need a unique number in the same millisecond but not necessarily otherwise; what are you generating so fast that in the same millisecond you could have a collision? I'm wondering how much time does uniqid() takes just to generate its number. If you want, use the prefix parameter of the uniqid() function with a few random letters and you should be safe.

If it's for generating file, you might want to look at tmpfile() or tempname().

In any case, depending on what you are trying to achieve, you can just loop and verify if the unique id is already taken (in an array, with file_exists, etc.) and just generate another one if it's the case.


Also, as I'm not sure I understand your question exactly, I would point you to those other questions that sound pretty similar while I get the difference of yours:

The first one will be of interest if you are looking to do a unique id that's human readable. The second one could be useful if you want to play with random numbers and md5/sha1. Although, again, I think uniqid() might already be what you are looking for.

Related