1

tl:dr version:

  • Is the first code secure enough to use on a live site?
  • How do I implement RandomLib? Clear instructions for beginners please.

I appreciate this question has been asked before but there does not seem to be a clear answer and guidance on how to use the recommended method.

I need to create random reset codes where a user has forgotten their password. This reset code is then added to the URL which is emailed to them. When they click on the URL the reset code and email is compared to the code and email in the database and if a match, a reset form is offered.

My first attempt at creating the code is simple, but is it secure enough? How easy would it be to hack? I will be asking people to register on the site so I need to make the system secure.

<php?
$bytes = openssl_random_pseudo-bytes(3);
$reset_code = bin2hex($bytes);

I've seen a lot of recommendations on this site and others for RandomLib https://github.com/ircmaxell/RandomLib but I cannot work out how to use this and I have seen others struggle with no guidance. I have downloaded the zip and added to my website documents. I either receive an error message "Fatal error: Class 'RandomLib\Factory' not found" or when I link to the factory.php file with "require_once("RandomLib\factory.php");" I receive an error "Fatal error: require_once(): Failed opening required 'RandomLibactory.php' (include_path='.;C:\php\pear')"

require_once("RandomLib\factory.php");
$factory = new RandomLib\Factory;
$generator = $factory->getMediumStrengthGenerator(8, alphabet);

In short, if the RandomLib is the best option, it would be helpful if someone could offer guidance on how to use it for beginners in a position similar to myself. Or if the first option is secure enough, I will leave it as it is.

UPDATE:

As pointed out by Narf below, the function to create the random string was missing the second parameter which ensures the generated string is secure.

openssl_random_pseudo-bytes(3);

It should read:

openssl_random_pseudo-bytes( 3 , $cstrong );

Where the second parameter $cstrong returns a boolean which is true if it is secure but false if not. http://php.net/manual/en/function.openssl-random-pseudo-bytes.php

user4612360
  • 129
  • 2
  • 13
  • Have a look on that question: http://stackoverflow.com/questions/6585649/php-forgot-password-function. That is an other very secure way to do it. – inetphantom Aug 11 '15 at 16:10
  • I wrote a small [php class](http://www.martinstoeckli.ch/php/php.html#passwordreset) which helps generating and handling such random tokens. The function `bin2hex()` will make your tokens longer than necessary, a base62 encoding is more compact. – martinstoeckli Aug 12 '15 at 07:52

1 Answers1

2

First things first, the term "hack" is a bit inappropriate. I know it sounds nitpicky, but that's very important in order to understand the problem. When you're generating pseudo-random strings, you need them to be unpredictable - that's the difference between a "secure" random string and one that just looks like random (i.e. a hash of a timestamp is NOT unpredictable).

That being said, you've started very well - openssl_random_pseudo_bytes() is an appropriate tool for your goal. But, depending on the environment, it may not be enough (which is why you haven't found a clear answer).

The problem is multi-faceted ...

  • The function has a second argument that allows you to check if its output is really "secure", and you're not checking that.
  • Due to OpenSSL implementation details, other possible sources of randomness are considered better by most security experts (i.e. mcrypt_create_iv() or reading straight from /dev/urandom, but the latter is tricky).
  • Because it depends on the environment (OS-level resources), there's no guarantee that it would be as safe on your production server as it is on your dev machine and it's easy to forget checking that if you move your application to another host in the future.

RandomLib exists mostly because of the above-listed reasons - it will pick the most appropriate source of randomness for your environment, do all the necessary checks and trigger an error if no "secure" randomness source is available. The problem with RandomLib is ... I can't help you with it, sorry. I haven't used it myself, but regardless - the error messages that you've posted clearly show that you're just passing the wrong filepath to require(). If you posted that as a separate question - it would be closed because it is considered off-topic on StackOverflow; you just need to get that one right on your own.

However, PHP7 (yet to be released as of the time of this post) now has a random_bytes() function, which abstracts all these concerns for you and there's a project that provides a user-space backport of that function. It's still beta (again, as I write this), but you should deffinately follow it and put it to use once the 1.0 version is released. Here: https://github.com/paragonie/random_compat

Narf
  • 14,600
  • 3
  • 37
  • 66
  • Thanks for the reply and explanation. It's really helpful when people explain it as you have. I'm glad I have started on the right lines with the first function. PHP 7 sounds promising as hopefully it will be slightly easier for people like myself to understand. I will update the original post with the second parameter for the function assuming this is what you were suggesting? The mcrypt_create_iv() seems a little complex for me at this stage. I do use BCRYPT to hash the random codes before inserting into the database so hopefully this will make it 'secure enough'. Thanks again! – user4612360 Aug 11 '15 at 19:38
  • bcrypt is for password storage, where you need to prevent brute-force attacks, but that's because passwords can be relatively easy to guess. You don't need that for a random token (but you do need to extend its length, 3 bytes aren't enough; try with 16+). – Narf Aug 11 '15 at 22:38