Reputation: 145097
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.
Upvotes: 40
Views: 73127
Reputation: 117403
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.
Upvotes: 2
Reputation: 6660
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
Upvotes: 58
Reputation: 342
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);
Upvotes: -1
Reputation: 121
$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.
Upvotes: 0
Reputation: 517
Why not use openssl_random_pseudo_bytes http://www.php.net/manual/en/function.openssl-random-pseudo-bytes.php
Upvotes: 4
Reputation: 1133
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.
Upvotes: 29
Reputation: 7316
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.
Upvotes: 9
Reputation: 13211
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.
Upvotes: 0