34

Using PHP, what are some ways to generate a random confirmation code that can be stored in a DB and be used for email confirmation? I can't for the life of me think of a way to generate a unique number that can be generated from a user's profile. That way I can use a function to make the number small enough to be included in the URL (see this link). Remember, the user has to click on the link to "confirm/activate" his/her account. If I can't use numbers, I have no problems using both letters and numbers.

With that said, I've tried hashing the username along with a "salt" to generate the random code. I know there has to be a better way, so let's hear it.

John Conde
  • 217,595
  • 99
  • 455
  • 496
sdot257
  • 10,046
  • 26
  • 88
  • 122

5 Answers5

58
$random_hash = md5(uniqid(rand(), true));

That will be 32 alphanumeric characters long and unique. If you want it to be shorter just use substr():

$random_hash = substr(md5(uniqid(rand(), true)), 16, 16); // 16 characters long

Alternative methods to generate random data include:

$random_hash = md5(openssl_random_pseudo_bytes(32));
$random_hash = md5(mcrypt_create_iv(32, MCRYPT_DEV_URANDOM));

// New in PHP7
$random_hash = bin2hex(random_bytes(32));
John Conde
  • 217,595
  • 99
  • 455
  • 496
  • 1
    +1 Nice and unique and not based on user data. That said, I *think* the rightmost portion of uniqid is more unique that the left, so you'd probably want to use -16, 16 for the substr. – John Parker Jan 18 '10 at 20:36
  • 18
    And before the inevitable question comes up: You would have to hash about 18,000,000,000,000,000,000 items before you had a 50% likelyhood of getting two of the same hash. That's one hash every millisecond for 584 million years. So yes, they will be unique. – BlueRaja - Danny Pflughoeft Jan 18 '10 at 20:38
  • Yea, thanks guys! This along w/ querying against the hash code and username should be good enough, no? – sdot257 Jan 18 '10 at 20:39
  • The randomness of the openssl_random_pseudo_bytes function is way too better than of rand(). – akond Jun 07 '11 at 13:04
  • @middaparka: Good thought! But since the substr is applied to the hash (md5), not the uniqid, it would not make a difference in this case. – Levite Apr 09 '14 at 11:37
  • I want more explanation about why these methods will generate a unique string. Thanks :) – Alston Feb 03 '19 at 15:21
10

1) Create an Activated Field in Database

2) After registration the Email is sent

3) Create a Link to include in Email,Use a Unique identifier It would look something like this

Welcome Username Thanks for registering.

Please Click on the Link below to activate your account

domain.com/register.php?uid=100&activate=1

4) Update the Activated Field to true

alt text
(source: jackborn.com)

$email_encrypt = urlencode($email);
$special_string = 'maybeyourcompanynamereversed?';
$hash = md5($email_encrypt.$special_string);

Here is the link that is sent to the email that was provided:

http://yourdoman.com/confirm.php?hash='.$hash.'

The actual link will look something like this:

http://yourdomain.com/confirm.php?hash=00413297cc003c03d0f1ffe1cc8445f8
Glorfindel
  • 21,988
  • 13
  • 81
  • 109
streetparade
  • 32,000
  • 37
  • 101
  • 123
  • +1 for the pic as well as the code. Using the DB is helpful to to keep track of the codes... and handle expiration etc. – scunliffe Jan 18 '10 at 22:06
  • But due to security reasons you need to delete the activation records – indianwebdevil Nov 12 '11 at 18:34
  • Good step-by-step explanation, but be aware that if either the value of `$special_string` (or your source-code) is known publicly, anyone could register any email-address and create the corresponding hash. Or in other words: could activate any email-address without owning it. – Levite Apr 09 '14 at 11:46
  • -1 this is incomplete and not possible: how do you activate the account on the backend, you cannot recover the email from the hash – albanx Jul 05 '18 at 22:48
4

The accepted answer suggests using a hash of PHP's uniqid(). The documentation for uniqid explicitly warns that it does not create "random nor unpredictable strings", and states emphatically that "This function must not be used for security purposes."

If there is any concern over the possibility of a confirmation code being guessed (and that is the whole point of issuing a code) you may wish to use a more random generator such as openssl_random_pseudo_bytes(). You can then use bin2hex() to turn it into a nice alphanumeric. The following looks just like the output of John Conde's answer, but is (supposedly) more random and less guessable:

// generate a 16 byte random hex string
$random_hash = bin2hex(openssl_random_pseudo_bytes(16))

Late addendum: As Oleg Abrazhaev points out, if you want to make sure your system is actually capable of generating cryptographically strong random values at runtime, openssl_random_pseudo_bytes accepts a reference to a bool to report this. Code from phpinspectionsea docs:

$random = openssl_random_pseudo_bytes(32, $isSourceStrong);
if (false === $isSourceStrong || false === $random) {
    throw new \RuntimeException('IV generation failed');
}

Then use the generated random value as before:

$random_hash = bin2hex($random)
Robert
  • 6,660
  • 5
  • 39
  • 62
3

Decided I need something a little more robust and added functionality. So this is what I came up with.

/**
 * Hash Gen 
 * @author Kyle Coots
 * @version    1.0
 * Allow you to create a unique hash with a maximum value of 32.
 * Hash Gen uses phps substr, md5, uniqid, and rand to generate a unique 
 * id or hash and allow you to have some added functionality.
 * 
 * @see subtr()
 * @see md5()
 * @see uniqid()
 * @see rand()
 *  
 * You can also supply a hash to be prefixed or appened
 * to the hash. hash[optional] is by default appened to the hash 
 * unless the param prefix[optional] is set to prefix[true].     
 * 
 * @param start[optional]
 * @param end[optional]
 * @param hash[optional]
 * @param prefix bool[optional]
 * 
 * @return string a unique string max[32] character
 */
function hash_gen($start = null, $end = 0, $hash = FALSE, $prefix = FALSE){

    // start IS set NO hash
    if( isset($start, $end) && ($hash == FALSE) ){

        $md_hash = substr(md5(uniqid(rand(), true)), $start, $end);
        $new_hash = $md_hash;

    }else //start IS set WITH hash NOT prefixing
    if( isset($start, $end) && ($hash != FALSE) && ($prefix == FALSE) ){

        $md_hash = substr(md5(uniqid(rand(), true)), $start, $end);
        $new_hash = $md_hash.$hash;

    }else //start NOT set WITH hash NOT prefixing 
    if( !isset($start, $end) && ($hash != FALSE) && ($prefix == FALSE) ){

        $md_hash = md5(uniqid(rand(), true));
        $new_hash = $md_hash.$hash;

    }else //start IS set WITH hash IS prefixing 
    if( isset($start, $end) && ($hash != FALSE) && ($prefix == TRUE) ){

        $md_hash = substr(md5(uniqid(rand(), true)), $start, $end);
        $new_hash = $hash.$md_hash;

    }else //start NOT set WITH hash IS prefixing
    if( !isset($start, $end) && ($hash != FALSE) && ($prefix == TRUE) ){

        $md_hash = md5(uniqid(rand(), true));
        $new_hash = $hash.$md_hash;

    }else{

        $new_hash = md5(uniqid(rand(), true));

    }

    return $new_hash;

 } 
Kyle Coots
  • 2,041
  • 1
  • 18
  • 24
-1
  private  function generateCodeSecurity()
  {
    list($usec, $sec) = explode(" ", microtime());
    $micro = usec + $sec;

    $hoy = date("Y-m-d");  
    $str = str_replace('-','',$hoy); 

    return  rand($str,  $micro);

  }

With this little code, you can generate a random number, with a range of 7 to 11 numbers.

Using php functions:

Rand ();
Microtime ()



$hoy = date("Y-m-d");  
$str = str_replace('-','',$hoy); 

echo $str; 
result date: 20170217



 list($usec, $sec) = explode(" ", microtime());
 $micro = usec + $sec;


echo $micro;
result  micro varaible: 1487340849

Passing parameters in this function:rand ();

 rand($str,  $micro);

and return;

example:

 list($usec, $sec) = explode(" ", microtime());
    $micro = usec + $sec;

    $hoy = date("Y-m-d");  
    $str = str_replace('-','',$hoy); 

   $finalresult = rand($str,  $micro);

echo $finalresult; 

result: 1297793555

I think it is difficult to repeat this number, for the reason it will never be the same day, nor the same hour, nor the same milliseconds of time.

  • 2
    Whilst this code snippet is welcome, and may provide some help, it would be [greatly improved if it included an explanation](//meta.stackexchange.com/q/114762) of *how* and *why* this solves the problem. Remember that you are answering the question for readers in the future, not just the person asking now! Please [edit] your answer to add explanation, and give an indication of what limitations and assumptions apply. In particular, you ought to explain that this is extremely guessable, and not suitable for the intended purpose. – Toby Speight Feb 16 '17 at 15:49