generating thousands unique unpredictable serial numbers - php

Hi I want to generate thousands of unique unpredictable serials, so I wrote this function
function generate_serials($count){
$serials = array();
$token = '8uhf7+-=??/\/';
for($i = 1; $i <= $count; $i++){
$hash = sha1(md5( $token . ( time() + (432 * 1000) ) ));
$serial = '';
for($j = 0; $j < 12; $j++){
if($j == 4 || $j == 8) $serial .= '-';
$serial .= $hash[rand(0, 39)];
if(in_array($serial, $serials)){
$serial = '';
$j = 0;
}
}
$serials[] = $serial;
}
return $serials;
}
I have few problems about this approach.
I don't know why, but I don't trust this. (As I always think my solutions and I are stupid) is there any problem with this?
another problem is with performance that it takes about 20-30 seconds, Is it natural or I made a mess?
UPDATE
Oh sorry guys
the problem of performance was that the counter for inner loop was the same name as outer loop ..my mistake! :D

Your solution is overcomplicated. It doesn't have to be that way. If you have OpenSSL installed look into openssl_random_pseudo_bytes
Alternatively you could use mt_rand and hash like so:
$serial = hash('sha512', mt_rand());
However mt_rand does not generate cryptographically secure values so it may not be as unpredictable as you wish.

Related

Random generator returning endless duplicates

I am trying to create a random string which will be used as a short reference number. I have spent the last couple of days trying to get this to work but it seems to get to around 32766 records and then it continues with endless duplicates. I need at minimum 200,000 variations.
The code below is a very simple mockup to explain what happens. The code should be syntaxed according to 1a-x1y2z (example) which should give a lot more results than 32k
I have a feeling it may be related to memory but not sure. Any ideas?
<?php
function createReference() {
$num = rand(1, 9);
$alpha = substr(str_shuffle("abcdefghijklmnopqrstuvwxyz"), 0, 1);
$char = '0123456789abcdefghijklmnopqrstuvwxyz';
$charLength = strlen($char);
$rand = '';
for ($i = 0; $i < 6; $i++) {
$rand .= $char[rand(0, $charLength - 1)];
}
return $num . $alpha . "-" . $rand;
}
$codes = [];
for ($i = 1; $i <= 200000; $i++) {
$code = createReference();
while (in_array($code, $codes) == true) {
echo 'Duplicate: ' . $code . '<br />';
$code = createReference();
}
$codes[] = $code;
echo $i . ": " . $code . "<br />";
}
exit;
?>
UPDATE
So I am beginning to wonder if this is not something with our WAMP setup (Bitnami) as our local machine gets to exactly 1024 records before it starts duplicating. By removing 1 character from the string above (instead of 6 in the for loop I make it 5) it gets to exactly 32768 records.
I uploaded the script to our centos server and had no duplicates.
What in our enviroment could cause such a behaviour?
The code looks overly complex to me. Let's assume for the moment you really want to create n unique strings each based on a single random value (rand/mt_rand/something between INT_MIN,INT_MAX).
You can start by decoupling the generation of the random values from the encoding (there seems to be nothing in the code that makes a string dependant on any previous state - excpt for the uniqueness). Comparing integers is quite a bit faster than comparing arbitrary strings.
mt_rand() returns anything between INT_MIN and INT_MAX, using 32bit integers (could be 64bit as well, depends on how php has been compiled) that gives ~232 elements. You want to pick 200k, let's make it 400k, that's ~ a 1/10000 of the value range. It's therefore reasonable to assume everything goes well with the uniqueness...and then check at a later time. and add more values if a collision occured. Again much faster than checking in_array in each iteration of the loop.
Once you have enough values, you can encode/convert them to a format you wish. I don't know whether the <digit><character>-<something> format is mandatory but assume it is not -> base_convert()
<?php
function unqiueRandomValues($n) {
$values = array();
while( count($values) < $n ) {
for($i=count($values);$i<$n; $i++) {
$values[] = mt_rand();
}
$values = array_unique($values);
}
return $values;
}
function createReferences($n) {
return array_map(
function($e) {
return base_convert($e, 10, 36);
},
unqiueRandomValues($n)
);
}
$start = microtime(true);
$references = createReferences(400000);
$end = microtime(true);
echo count($references), ' ', count(array_unique($references)), ' ', $end-$start, ' ', $references[0];
prints e.g. 400000 400000 3.3981630802155 f3plox on my i7-4770. (The $end-$start part is constantly between 3.2 and 3.4)
Using base_convert() there can be strings like li10, which can be quite annoying to decipher if you have to manually type the string.

Long digit random number create algorithm

I want to create long digit random number in php but there is one question on my mind. What is the best way for performance.
For example first option that i thought.
rand(10000,99999)
but i don't know how would performance be with this. how would it be if even longer?
so i think of this
$randomNumber=rand(10,99).rand(10,99).rand(10,99)...
is it more efective than first one?
Thank you.
You can use MD5() or if you are using mysql then UUID(). otherwise rand(10000,99999) is OK .
Try this
function RandomNumber()
{
$size = '15';
$randomnumber = '';
for ($i = 0; $i < $size ; $i++) {
$randomnumber .= rand(0,9);
}
return $randomnumber;
}
$random = RandomNumber();
echo $random;
rand(10000,99999)
use of above function is pragmatically and logically very good advise than other options

Any Security holes in this php code?

i found the structure of the following code on this very site, and i'm now using it, so i just want to know if there are any security holes that can be exploited from this code, if it can be improved or if at all there are any deprecated elements i shouldn't use.
i'll be using the following for OTP Code through SMS.
<?php
function randomCode(){
$alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
$Code = array();
$alphaLength = strlen($alphabet) - 1;
for ($i = 0; $i < 6; $i++){
$n = rand(0, $alphaLength);
$Code[] = $alphabet[$n];
}
return implode($Code);// turn array into string
}
echo randomCode();
?>
Beginner
upd.
I rewrote the code a bit:
function randomCode(){
$alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
$Code = "";
$alphaLength = strlen($alphabet) - 1;
for ($i = 0; $i < 6; $i++){
$Code .= $alphabet[rand(0, $alphaLength)];
}
return $Code;
}
echo randomCode();
upd2.
This would be secure, if you block this code after 3 wrong attempts.
Yes, you should not use insecure random number generators for security purposes.
From http://php.net/manual/en/function.rand.php
Caution This function does not generate cryptographically secure
values, and should not be used for cryptographic purposes. If you need
a cryptographically secure value, consider using
openssl_random_pseudo_bytes() instead.
This is because the rand will generate predictable values, the attacker only needs to figure out the seed used.
See here: Predicting the output of PHP's rand()
use more charecters like # or # and lowercase letters in your $alphabet var.

Random numbers with rand() and one seed

I have to generate a large number of unique keys. One key should consist of 16 digits. I came up with the following code:
function make_seed()
{
list($usec, $sec) = explode(' ', microtime());
return (float) $sec + ((float) $usec * 100000);
}
function generate_4_digits(){
$randval = rand(100, 9999);
if($randval < 1000){
$randval = '0'.$randval;
}
return (string)$randval;
}
function generate_cdkey(){
return generate_4_digits() . '-' . generate_4_digits() . '-' . generate_4_digits() . '-' . generate_4_digits();
}
srand(make_seed());
echo generate_cdkey();
The result was quite promising, 6114-0461-7825-1604.
Then I decided to generate 10 000 keys and see how many duplicates I get:
srand(make_seed());
$keys = array();
$duplicates = array();
for($i = 0; $i < 10000; $i++){
$new_key = generate_cdkey();
if(in_array($new_key, $keys)){
$duplicates[] = $new_key;
}
$keys[] = $new_key;
}
$keys_length = count($keys);
var_dump($duplicates);
echo '<pre>';
for($i = 0; $i < $keys_length; $i++){
echo $keys[$i] . "\n";
}
echo '</pre>';
On first run I got 1807 duplicates which was quite disappointing. But for my great surprise on each following run I get the same number of duplicates!? When I looked closely at the generated keys, I realized the last 1807 keys were exactly the same as the first ones. So I can generate 8193 without a single duplicate?! This is so close to 2^13?! Can we conclude rand() is suited to generate maz 2^13 unique numbers? But why?
I changed the code to use mt_rand() and I get no duplicates even when generating 50 000 keys.
This is probably more what you're looking for. openssl_random_pseudo_bytes ( int $length [, bool &$crypto_strong ] )
Throw some uniquid() in there.
http://www.php.net/manual/en/function.uniqid.php
This might be something to do with the behaviour of srand. When checking for duplicates you are only running srand once for all 10000 keys. Perhaps srand only produces enough for ~2^13 keys? What PHP version are you using? Since 4.2.0 srand isn't needed any more, but perhaps in if you call it anyway it stops doing it automatically for the rest of the script.

Random Code Overkill?

I have some code I am using
function genCode ($entropy=1) {
$truCde = "";
$indx = 0;
$leng = 30*$entropy;
while ($indx < $leng) {
$code = "";
$length = 100*$entropy;
$index = 0;
while ($index < $length) {
$code .= rand();
$index++;
}
$index = 0;
while ($index < $length) {
$code = sha1($code);
$index++;
}
$truCde .= $code;
$indx++;
}
$finalCode = sha1(rand()) . hash("sha256",$truCde . md5($entropy*rand()));
$finalCode .= sha1(md5(strlen($finalCode)*$entropy));
return hash (
"sha256",
sha1($finalCode) . sha1(md5($finalCode)) . sha1(sha1($finalCode))
);
}
to generate a random code for e-mail verification. Is there code that takes less time to generate random codes. It takes about 1-2 seconds to run this code, but I am looking to shave .7 seconds off this because the rest of the script will take longer.
That's massive overkill. Calling rand() repeatedly isn't going to make the code "more random", nor will using random combinations of SHA and MD5 hashes. None of that complexity improves the verification codes.
An improvement that would make a difference would be to use mt_rand() in preference to rand(). The Mersenne Twister pseudo RNG is much stronger than most default rand() implementations. The PHP documentation hints that rand() may max out at 215 meaning you can only generate 32,768 unique verification codes.
Other than that, a single hash call will do.
sha1(mt_rand())
(You don't even really need to call a hash function as the unpredictability of your codes will come from the random number generator, not the hash function. But hash functions have the nice side effect of creating long hex strings which "look" better.)
If you just want to generate random strings to test that someone has access to an email address, or something like that, I would throw out that code and use something a lot more straightforward. Something like the following would likely do.
function genCode () {
$chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
$returnValue = '';
for ($i = 0; $i < 20; $i++) {
$returnValue .= $chars[mt_rand(0, 35)];
}
return $returnValue;
}
You can hash the return value if you want, but I don't know what the point would be other than to obfuscate the scheme used to come up with the random strings.

Categories