1

I would like to create a token in PHP that can be used for a confirmation email as part of a password reset request.

When searching for this online I came across a post here where I found the following:

$token = md5(uniqid(mt_rand(), true));

Can someone tell me if this should be preferred here compared to the below and let me know what are the differences here ?

$token = bin2hex(openssl_random_pseudo_bytes(16));

OR

$token = bin2hex(mcrypt_create_iv(128, MCRYPT_DEV_RANDOM));

Also, can you let me know which datatype should be used to store this in the db (using MySQL) ?

Many thanks in advance, Mike

Community
  • 1
  • 1
keewee279
  • 1,656
  • 5
  • 28
  • 60
  • @meh: Thanks a lot for this. Can you also let me know which data type I should use to store this in the db ? If you want to post it as an answer than I'll accept this. – keewee279 Jul 12 '15 at 06:30
  • By the way, `mcrypt_create_iv(16, MCRYPT_DEV_URANDOM);` gives you 16 bytes (128 bits). – Scott Arciszewski Jul 15 '15 at 04:13

1 Answers1

6

Do not use uniqid() as a random value in any security-critical code. This includes password resets — an attacker may be able to predict the value of uniqid(), permitting them to guess a security token and reset the password of any user on your system.

Instead, use bin2hex(openssl_random_pseudo_bytes(…)) (as seen in your question) to create a secure random token. The PHP manual specifically recommends this function for the purpose in a footnote to the entry on uniqid():

This function [that is, uniqid()] does not generate cryptographically secure tokens, in fact without being passed any additional parameters the return value is little different from microtime(). If you need to generate cryptographically secure tokens use openssl_random_pseudo_bytes().

As the result of bin2hex() is a printable string, you can store it in your database using any string type. VARCHAR should be your default choice here, and it's perfectly appropriate.


For maximum security, you may want to take one additional step. Instead of storing the token in the database, store a hash of the token (e.g, sha1($token)) in the database, then compare the hash of the value presented by the user with the stored hash. In essence, treat it like a password! This extra step will prevent an attacker that has the ability to read your database, but not to write to it, from gaining control over a user's account by reading their "forgot password" token from the database.

  • Thanks a lot - this is really helpful ! So for the token should I use $token = bin2hex(openssl_random_pseudo_bytes(16)); as in my answer or do I have to add or change anything there ? – keewee279 Jul 12 '15 at 06:52
  • Also, I like the idea of storing a hash of the token. What data type do I have to use (in MySQL) when storing it the way you suggested ? – keewee279 Jul 12 '15 at 06:54
  • 1
    Yes, the second chunk of example code given in your question is good as it stands. With regard to storing a hash, `VARCHAR` is, again, perfectly fine. (There is, in general, rarely any need for any other string type. In particular, you can safely ignore `CHAR` entirely, outside of some very special circumstances.) –  Jul 12 '15 at 06:55
  • Awesome - thanks ! And what length should I use for the VARCHAR ? – keewee279 Jul 12 '15 at 06:56
  • 1
    Long enough to fit the values you are storing in the column. :) So, at least 32 characters if you're storing the bin2hex value directly, or 40 characters for a SHA1 hash. –  Jul 12 '15 at 06:58
  • Thanks again - you really helped me out here and it helped me to understand the background of this much better ! :) – keewee279 Jul 12 '15 at 06:59