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

8

40

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.

Coma answered 12/3, 2009 at 3:41 Comment(3)
Random number generators don't generally guarantee uniqueness, even if “generated at the exact same millisecond.” To guarantee uniqueness, you need to be able to detect a collision.Northeaster
Added big warning about the security, otherwise I would have to downvote each and every answer. Be warned that some of the functions in the answers return binary, others return hex, others base64 etc. etc.Embryonic
If you really want all ASCII printable characters, Random::asciiPrintableString($length) does exactly what you need. If you only want letters and numbers, use Random::alphanumericString($length) instead.Caren
F
58

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][1]):

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) [1]: Short unique id in php

Footpoundsecond answered 12/3, 2009 at 4:5 Comment(8)
Beat ya just by a few seconds.Coma
Beat ya just by a few months ;)Coition
+1 for the clean answer and modification to the original code, this one is better using mt_rand()Coition
Not to clutter the answer anymore, I added a simple Uniqid test and after many runs, it averaged about 0.165093898773, which is significantly faster than the other three. Remember though it returns 13 digits of hex + random numbers, which is a horrible charset!Footpoundsecond
Nice tests, I'd +1 again if I could. What would it give by combining sha1(uniqid()) or something like that? Unique + fixed characters and length.Coition
Im trying to generate a process in order to create like 5000 inserts into a table with rand_md5 function here presented but sometimes it gives a repeated "unique" key. What can I do to solve this?Lipoid
Can you update this answer to emphasize the need for random_bytes() instead of rand()?Kirkman
@ScottArciszewski if you wanted to keep this style of answer, I would use random_int() instead. Otherwise you need to convert from the bytes into an ascii style output.Footpoundsecond
S
29

None of the answers here are sufficient if you want cryptographic-strength randomness (is there a determined attacker trying to guess what your random keys are?). Hashing the time is not secure, an attacker can greatly speed up their search by guessing around the time they think your server generated the key, and it's easy to search over all milliseconds in a given year, even on a commodity laptop (it's a 35 bit search space). Also, the suggestion to just run the results of uniqid() or some other weak random source through a hash function to "expand it" is dangerous-this doesn't make an attacker's search harder once they find out that you did this.

If you really need crypto-level security, you should read from /dev/random, the following code should work for you in any POSIX-compliant system (anything but Windows):

#Generate a random key from /dev/random
function get_key($bit_length = 128){
    $fp = @fopen('/dev/random','rb');
    if ($fp !== FALSE) {
        $key = substr(base64_encode(@fread($fp,($bit_length + 7) / 8)), 0, (($bit_length + 5) / 6)  - 2);
        @fclose($fp);
        return $key;
    }
    return null;
}

If you need a bit more speed, you can read from 'dev/urandom' instead.

Schizont answered 11/5, 2011 at 0:4 Comment(2)
Just wanted to note - I had a script that was taking an extra 50s or so to load, and it turns out using /dev/random instead of /dev/urandom was the cause. On my local test environment, this never happened.Physic
/dev/urandom should be the default, using /dev/random/ will deplete the random entropy pool too soon (on systems without a dedicated random number generator linked to /dev/random). Actually, as it would be a nice way for an attacker to perform a DoS attack, I don't think the function should be exposed at all, it is only useful for applications/libraries with their own PRNG.Embryonic
C
9

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.

Clifford answered 12/3, 2009 at 4:11 Comment(0)
M
3

Why not use openssl_random_pseudo_bytes http://www.php.net/manual/en/function.openssl-random-pseudo-bytes.php

Master answered 2/12, 2013 at 12:12 Comment(2)
This answer would be more effective if you showed an example of how it would help.Benniebenning
I am successfully using the above as follows $sessionkey = bin2hex(openssl_random_pseudo_bytes(1024)); This generates a cryptographically safe random key ... This is an answer to .. 'the best way to generate a random key'... It's a very good way as .. a) it uses a simple quick function ... it's crypo safe, it's obviously random.. and you can generate keys of any length...Master
M
2

Updated 2021

Since PHP 7.0, PHP has a dedicated function for generating a random key of arbitrary length: random_bytes().

Unlike previous solutions, even including openssl_random_pseudo_bytes(), random_bytes() will only ever return data that is suitable for cryptographic use. Its random source is provided by the operating system, and therefore the implementation depends on the OS. PHP will throw an exception if no source of random data suitable for cryptographic use is available.

Previous answers to this question attempt to create unique identifiers using sources such as uniqid() and hashes, but that doesn't generate a random key, only one that is highly likely to be unique. Similarly, the output from rand() or mt_rand() is not random enough for cryptographic purposes. This question was asked before either random_bytes() or its predecessor openssl_random_pseudo_bytes() were available in PHP.

Note that random_bytes() returns raw 8-bit binary data. If you wish to make this data printable, you will need to use an encoding like base64_encode() or bin2hex().

A similar function, random_int() also exists which uses the same random source, but outputs as an integer. Of course, this means the number of possible keys output is limited to the size of an integer, so it won't be suitable for something like an encryption key on its own.

Manas answered 3/3, 2015 at 23:13 Comment(0)
C
0

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.

Coition answered 12/3, 2009 at 3:54 Comment(0)
A
0

$key = md5(microtime() . rand());

Microtime by itself isn't safe because it leaves only 1/1000 chance of being guessed.

Rand by itself is also not very secure, but hashing their concatenating them together yields a pretty solid randomization.

Auspicious answered 10/4, 2014 at 18:2 Comment(1)
"Rand by itself is also not very secure, but hashing their concatenating them together yields a pretty solid randomization." Nope. You have a predictable value with an upper limit of 32 bits of entropy from rand(). If we assume microtime() contains 10 bits of entropy, you end up with about 42 bits. Birthday collisions after every 2 million samples, but rand() is predictable. Better solution: Use a CSPRNG for generating keys.Kirkman
S
-1

Also you can generate a random key and check the key in your DB if the key is exits, you can generate another key by this method

        function activation($lengt=20){
    $modalpage =new Modal;//you can call your modal page and check the key

    $characters = "1234567890abcdefghijklmnopqrstuvwxyz";
    for($i=0;$i<$lengt;$i++)

    {    

    $key .= $characters{rand(0,35)};
    $check= array(
     ":key" => $key,
    );

     $result = $modalpage->findkey($check);

     if($result==true){ //if the key is exits return the function and generate another key !

        return activation($lengt);   

     }else //if the key is not exits return the key
    return $key;
    }


            } 

$mykey=activation(15);
Sumach answered 20/4, 2015 at 20:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.