Reputation: 1753
I need a valid way to get both a random and unique string, with either a zero (or negligible) chance of a duplicate.
I need characters in the [0-9A-z]
range.
This is what I have so far:
substr(sha1(mt_rand().uniqid()),0,22);
Upvotes: 4
Views: 4002
Reputation: 3356
Why not just use the time()
function.
I needed to do something similar, a solution to keep unique id and i ended up with a solution to use PHP function time()
like this $reference_number = 'BFF-' . time();
you can change the BFF to something that makes more sense to your business logic. This way i dont have to worry about if new id that is being generated was taken up before since time is always unique
I hope this helps
Upvotes: -1
Reputation: 2970
Since I know this is actually talking about bcrypt and password salting now I can really just point people reading this to functions they should be using instead of manually rolling their own salt system.
Use password_hash($input, PASSWORD_DEFAULT);
to generate a hash suitable to insert into a database. This will fetch the salt for you.
Insertion:
$hash = password_hash($_POST["password"], PASSWORD_DEFAULT, ["cost" => 16]);
DB::table("users")->insert(["username" => $user, "password" => $hash]);
// or whatever database method you use to insert data
Verification:
$hash = DB::table("users")->fetchByName($username)->select("password");
$input = $_POST["password"];
$verified = password_verify($input, $hash); // true if the password matches
In versions before PHP 5.5, use https://github.com/ircmaxell/password_compat as a drop-in1
When randomly generating a salt, the odds of a collision are
1 / [number of possible letters/numbers] ** [length]
Which for a 22-character string are impossibly low (well, not impossibly, but negligibly)
1 / (22 ** 60) = 1 / (3.51043 x 10**80)
See? tiny.
If you need a truly random string (note: these strings are just a line of numbers mapped to letters), then you're a little out of luck.
What you're looking for is a CSPRNG (Cryptographically Secure Pseudo-Random Number Generator). No need for uniqueness.
As @Guarav pointed out in his answer, you can use a timestamp as your seed and then hash it. This is called a UUID (Unique Universal Identifier, if it's a 128bit timestamp) is predictable, and can be bad for a number of reasons:
Nevertheless, with enough accuracy, you can still use a timestamp as a unique salt. Not random (unless you use it as a random seed and base convert it to base10, which is still a bad idea). Consider this if you can count time in something under nanoseconds and fancy using it as a unique ID. PHP cannot feasibly process fast enough to give two colliding sub-nanosecond IDs1 (but that doesn't mean you shouldn't verify!)
1: It works with composer!
Upvotes: 8
Reputation: 4018
You won't guarantee that this will always return a unique value, but it will minimize the risk significantly. There is ALWAYS a possibility of duplicates when you use a random number generating algorithm.
To guarantee that only unique values are generated you can search against previously generated values and discard duplicates.
Some suggestions to reduce the chances of duplicates in your formula:
sha1
. This is a method that returns the one consistent output for any given input. It won't impact the possibility of duplicates.sha1
, consider converting the random number into a different base (for instance, numbers in base 36 can use the characters 0-9 and A-Z, which you can store in your database or whatever you're doing with the random string output). But I wouldn't really go for 36 here, maybe 256+?substr
you're actually increasing the chances of duplicates a little. Use what I wrote about in the previous bullet to convert the random number to a string with fewer than 22 characters that does not need to be truncated.substr
by returning a raw value with the sha1
function, which is only 20 characters.Upvotes: 1
Reputation: 28755
You can check the answer of my own question. How to generate Unique Order Id (just to show touser) with actual Order Id?
You can use TimeStamp instead of any random string.
Regarding your solution, there is possibility of collision, but its at very low level.
Upvotes: 2
Reputation: 26071
This is the way I am doing sometime...
// chars
$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()-+';
// convert to array
$arr = str_split($chars, 1);
// shuffle the array
shuffle($arr);
// array to chars with 22 chars
echo substr(implode('', $arr), 0, 22);
Output
xd*thKM$B#13^)9!QkD@gU
ixXYL0GEHRf+SNn#gcJIq-
$0LruRlgpjv1XS8xZq)hwY
$G-MKXf@rI3hFwT4l9)j0u
To make sure it is unique, you can always check in your database. In case it is repeated re generated the KEY.
Upvotes: 4