Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Secure random number generation in PHP

Use case: the "I forgot my password" button. We can't find the user's original password because it's stored in hashed form, so the only thing to do is generate a new random password and e-mail it to him. This requires cryptographically unpredictable random numbers, for which mt_rand is not good enough, and in general we can't assume a hosting service will provide access to the operating system to install a cryptographic random number module etc. so I'm looking for a way to generate secure random numbers in PHP itself.

The solution I've come up with so far involves storing an initial seed, then for each call,

result = seed seed = sha512(seed . mt_rand()) 

This is based on the security of the sha512 hash function (the mt_rand call is just to make life a little more difficult for an adversary who obtains a copy of the database).

Am I missing something, or are there better known solutions?

like image 667
rwallace Avatar asked Jul 25 '09 17:07

rwallace


People also ask

Is Mt_rand secure?

From http://php.net/manual/en/function.mt-rand.php: Caution This function does not generate cryptographically secure values, and should not be used for cryptographic purposes.

Is random choice () cryptographically secure?

Random numbers and data generated by the random class are not cryptographically protected. An output of all random module functions is not cryptographically secure, whether it is used to create a random number or pick random elements from a sequence.

How do you generate a non repeating random number in PHP?

php $check = array(); function generateNumber() { global $check; $page_no = mt_rand(1,20); $check[] = $page_no; if (count($check) != 1) { foreach ($check as $val) { if ($val == $page_no) { $page_no = mt_rand(1,10); continue; } } return $page_no; } else { return $page_no; } } ?>

What is Mt_rand function in PHP?

The mt_rand() function is a drop-in replacement for the older rand(). It uses a random number generator with known characteristics using the » Mersenne Twister, which will produce random numbers four times faster than what the average libc rand() provides.


Video Answer


2 Answers

I strongly recommend targeting /dev/urandom on unix systems or the crypto-api on the windows platform as an entropy source for passwords.

I can't stress enough the importance of realizing hashes are NOT magical entropy increasing devices. Misusing them in this manner is no more secure than using the seed and rand() data before it had been hashed and I'm sure you recognize that is not a good idea. The seed cancels out (deterministic mt_rand()) and so there is no point at all in even including it.

People think they are being smart and clever and the result of their labor are fragile systems and devices which put the security of their systems and the security of other systems (via poor advice) in unecessary jeopardy.

Two wrongs don't make a right. A system is only as strong as its weakest part. This is not a license or excuse to accept making even more of it insecure.


Here is some PHP code to obtain a secure random 128-bit string, from this comment at php.net by Mark Seecof:

"If you need some pseudorandom bits for security or cryptographic purposes (e.g.g., random IV for block cipher, random salt for password hash) mt_rand() is a poor source. On most Unix/Linux and/or MS-Windows platforms you can get a better grade of pseudorandom bits from the OS or system library, like this:

<?php // get 128 pseudorandom bits in a string of 16 bytes  $pr_bits = '';  // Unix/Linux platform? $fp = @fopen('/dev/urandom','rb'); if ($fp !== FALSE) {     $pr_bits .= @fread($fp,16);     @fclose($fp); }  // MS-Windows platform? if (@class_exists('COM')) {     // http://msdn.microsoft.com/en-us/library/aa388176(VS.85).aspx     try {         $CAPI_Util = new COM('CAPICOM.Utilities.1');         $pr_bits .= $CAPI_Util->GetRandom(16,0);          // if we ask for binary data PHP munges it, so we         // request base64 return value.  We squeeze out the         // redundancy and useless ==CRLF by hashing...         if ($pr_bits) { $pr_bits = md5($pr_bits,TRUE); }     } catch (Exception $ex) {         // echo 'Exception: ' . $ex->getMessage();     } }  if (strlen($pr_bits) < 16) {     // do something to warn system owner that     // pseudorandom generator is missing } ?> 

NB: it is generally safe to leave both the attempt to read /dev/urandom and the attempt to access CAPICOM in your code, though each will fail silently on the other's platform. Leave them both there so your code will be more portable."

like image 88
Einstein Avatar answered Oct 02 '22 14:10

Einstein


You can also consider using OpenSSL openssl_random_pseudo_bytes, it's available since PHP 5.3.

 string openssl_random_pseudo_bytes ( int $length [, bool &$crypto_strong ] ) 

Generates a string of pseudo-random bytes, with the number of bytes determined by the length parameter. It also indicates if a cryptographically strong algorithm was used to produce the pseudo-random bytes, and does this via the optional crypto_strong parameter. It's rare for this to be FALSE, but some systems may be broken or old.

http://www.php.net/manual/en/function.openssl-random-pseudo-bytes.php

Since PHP 7 there is also random_bytes function available

string random_bytes ( int $length ) 

http://php.net/manual/en/function.random-bytes.php

like image 34
Eugene Dounar Avatar answered Oct 02 '22 14:10

Eugene Dounar