I wrote this code to generate a random password.
$s = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
$len = strlen($s);
$m = '';
for ($i = 0; $i < $length; $i ++) {
$m .= $s[mt_rand(0, $len -1)];
}
return $m;
Is the returned password truly random?
Any PRNG such as the Mersenne Twister (hence the "mt" in the function name) is not truly random. But it is random enough for most purposes. If you need truly random then you should use your operating system's randomness facilities instead.
Some functions are specifically built to deal with cryptography-level randomness.
openssl_random_pseudo_bytes is a good one (requires the openssl lib obviously). You can use it to generate password with strong randomness.
MT (Mersenne Twister) used by mt_rand is considered a PRNG (Pseudo Random Number Generator) whereas openssl_random_pseudo_bytes will attempt to use a CSPRNG (Crypto Secure PRNG).
This function produces bytes which you can easily base64_decode to get passwords with the base64 character set.
Does all this really matter? Unless you're Facebook or a bank, probably not. mt_rand is probably good enough. This does serve to set you on a path to learn more about cryptography, if you're interested.
As others have pointed out, the concept of 'truly' random doesn't exist for computers. It would actually be quite disastrous if bits were indeed random even a little.
That being said, for most random string generators you probably want something that simulates sampling from a uniform distribution so each outcome is (in theory) equally likely. Different PRNG's simulate distributions with different degrees of success with the trade-off usually coming in the form of speed.
Assuming you are comfortable with mt_rand (I don't know of any reason to not be) and the standard printable ascii character set one simple approach that produces random strings each with equally likely characters across the full printable ascii set is this:
function create_random_string($len) {
$string = '';
for ($j = 0; $j < $len; $j++) {
$string = $string . chr(mt_rand(33,126));
}
return $string;
}
The meat of this is the rand(33,126) expression. It returns a decimal translatable to the printable ascii character set via chr as shown here: http://www.ascii-code.com/.
Each character is equally likely per the rand function making it as 'random' as possible with the broadest possible printable character set (again, assuming you are limiting the application to the ascii character set).
Related
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?
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."
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
PHP ships with a new set of CSPRNG functions (random_bytes() and random_int()). It's trivial to turn the latter function into a string generator function:
<?php
/**
* Generate a random string, using a cryptographically secure
* pseudorandom number generator (random_int)
*
* For PHP 7, random_int is a PHP core function
* For PHP 5.x, depends on https://github.com/paragonie/random_compat
*
* #param int $length How many characters do we want?
* #param string $keyspace A string of all possible characters
* to select from
* #return string
*/
function random_str(
$length,
$keyspace = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
) {
$str = '';
$max = mb_strlen($keyspace, '8bit') - 1;
if ($max < 1) {
throw new Exception('$keyspace must be at least two characters long');
}
for ($i = 0; $i < $length; ++$i) {
$str .= $keyspace[random_int(0, $max)];
}
return $str;
}
If you need to use this in a PHP 5 project, feel free to grab a copy of random_compat, which is a polyfill for these functions.
How about something like
<?
$length = 100;
$random = substr(hash('sha512', openssl_random_pseudo_bytes(128)), 0, $length);
I just noticed its about numbers, so heres the solution for numbers:
<?
$max = 1000;
$random = (unpack('n', openssl_random_pseudo_bytes(2))[1] * time()) % $max;
The standard way to create a cryptographically secure token using PHP seems to be:
$token = bin2hex(openssl_random_pseudo_bytes(16));
I understand if you're using Linux (which I always do) because this uses /dev/urandom — which is changed according all the many things that go on in the operating system —it makes it nigh impossible to predict.
My function is more like this so I can do it by char length rather than bit length (though I don't really ever use it, see below):
function token($charLength = 32) {
// Each byte produces 2 hexadecimal characters so bit length should be half the char length
$bitLength = $charLength / 2;
// Generate token
$token = bin2hex(openssl_random_pseudo_bytes($bitLength));
return $token;
}
Is it the unpredictability that makes it secure? I can't help thinking it's less secure because the output is hexadecimal and therefore is less hard to guess or brute-force than a string with the same number of chars that contains the rest of the alphabet, uppercase letters, other symbols, etc.
Is this why when people refer to tokens they refer to the bit length as opposed to char length?
Consider instead:
function randomString($length,
$alpha = true,
$alphau = true,
$numeric = true,
$specialChars = '') {
$string = $specialChars;
if($alpha === true) {
$string .= 'abcdefghijklmnopqrstuvwxyz';
}
if($alphau === true) {
$string .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
}
if($numeric === true) {
$string .= '0123456789';
}
$array = str_split($string);
$string = '';
for($counter = 0; $counter < $length; $counter ++) {
$string .= $array[array_rand($array)];
}
return $string;
}
In the context of web development when would you use the first function over the second for:
Creating a random password for a password reset
Creating a one-time use token (e.g. for a forgotten password link)
Creating a salt for a password hash (e.g. bcrypt, sha512, PBKDF2)
Creating a token for a “remember me” cookie token
In all instances I would use randomString() over token() so I guess I'm asking if and why I'm wrong in any of the above.
My rationale in relation to the above points:
12 char random password with uppercase, lower case and numbers is hard to guess; plus I freeze people out for 15 mins after 5 failed login attempts
64 char random string, If someone tried brute-forcing the token to reset a password the firewall would pick up on it
Salts should be assumed to be public anyway, so long as they're different per password it makes it impossible to produce a rainbow table
My remember me token is 128 char random string stored in a cookie and is salted and sha 512'd in the database
The primary concern with random number generators is generally not the output created, but the predictability in which this data is generated. Your basic question is why not use array_rand (which internally uses php_rand) over openssl_random_pseudo_bytes for cryptographic purposes. The answer has to do with the technique each function takes, with array_rand being a much more predictable (and reproduce-able) approach. See Pádraic Brady's article "Predicting Random Numbers In PHP – It’s Easier Than You Think!" for more detail: http://blog.astrumfutura.com/2013/03/predicting-random-numbers-in-php-its-easier-than-you-think/.
Concerning the output of random number generators, password/key strength in relation to brute force attacks is often measured in entropy. This is usually listed in bits with the more bits the better. The Wikipedia page on password strength (http://en.wikipedia.org/wiki/Password_strength) has some great reference tables for determining the entropy level of passwords at different lengths and using various combinations of character types. The openssl_random_pseudo_bytes() function utilizes all binary/hex values resulting in a full 8 bits of entropy per symbol. At best your randomString() function would result in 5.954 bits of entropy per symbol.
Use of a crypto strong random number should be used in all security related scenarios where the ability to guess one of these numbers would negatively affect your site in some manner. The only item in your list of 4 where I see a crypto strong random number not being required is with salt values for hashes. A salt value must be universally unique. It can certainly be produced by a crypto random number generator (CRNG), but this is not required as the resulting value can be made public. See https://security.stackexchange.com/questions/8246/what-is-a-good-enough-salt-for-a-saltedhash
I would like to generate a random byte sequence to be used in a security-sensitive context. I am looking for an OS-independent way, so I cannot access /dev/(u)random. Furthermore, openssl_random_pseudo_bytes() might not be available so let's forget about it too. I am also not interested in performance differences in any of the cases.
Looking at the phpass library (which is used by many and considered to be secure enough) to generate random sequences it falls back to to following algorithm if /dev/urandom is not available:
// Init:
$this->random_state = microtime();
if (function_exists('getmypid'))
$this->random_state .= getmypid();
// Generate
$count = ...number of bytes to generate
$output = '';
for ($i = 0; $i < $count; $i += 16) {
$this->random_state =
md5(microtime() . $this->random_state);
$output .=
pack('H*', md5($this->random_state));
}
$output = substr($output, 0, $count);
// $output ready
The above algorithm basically relies on getmypid(), md5() and microtime() to generate random sequences. It has been pointed out by many that time/microtime and pid are a bad source of entropy for secure applications, and so even if you transform these with md5 and some string operations, the output won't be 'securely random' either. So we know that the above algorithm is at best only as good as pid and microtime are as entropy sources.
In contrast here is another algorithm. Given that rand is seeded automatically by PHP, it is (probably) not better than the previous algorithm security-wise since rand/mt_rand are also seeded with computer time and pid by default (ref). But since it is much less complex, wouldn't it be a better alternative? I can also rephrase the question: given that both the algorithm above and that below rely on pid and computer time to generate random numbers, should the above still be superior and why?
function RandomBytes($len)
{
// Seed
$seed = (double)microtime()*1000003;
if (function_exists('getmypid'))
$seed += getmypid();
mt_srand($seed);
$output = '';
for ($i = 0; $i < $len; ++$i)
$output .= chr(mt_rand(0, 255));
// optionally transform using pack('H*', ...) to make output printable
return $output;
}
And as a last question, do you know a better (=more random) way than any of the above to generate random bytes in an OS-independent manner in PHP?
Be careful to rely on OS implemented pseudo-random generator, if strongly correctness is required. Specially for security and simulations. Pseudo random generators are not difficult to be implemented, have a look here for a bit of theory and some references.
You could attempt to use OS resources if they exist, and then fall back to something homegrown when these resources do not exist and you have no other choice. See this answer on another forum for an example.
On Windows systems that support .NET you can use the DOTNET php class together with the System.Security.Cryptography.RNGCryptoServiceProvider class.
The best random number generator you can possibly use is /dev/random. If this interface is not accessible then you maybe on a windows system in that case you can use the much less secure System.Security.Cryptography.RNGCryptoServiceProvider. The function mt_rand() should be a distant 3rd place in terms of how random its output is.
I need a big (like, say, 128-bit big) random number generator in PHP. I was thinking in storing this number in a string as hexadecimal.
Note that this is meant for a login system that mentioned the need for a "random" number, so I'm guessing I really need it to be "random-enough" (because I know pseudo-random is never truly random).
The algorithm I was thinking was generating the number one hex digit at a time, then concatenating it all. Like this:
$random = '';
for ($i = 0; $i < 32; ++$i) {
$digit = rand(0, 15);
$random .= ($digit < 10 ? $digit : ($digit - 10 + 'a'));
}
return $random;
Can I trust this function to return good pseudo-random numbers or am I messing with something I really shouldn't?
Try:
for ($str = '', $i = 0; $i < $len; $i++) {
$str .= dechex(mt_rand(0, 15));
}
I asked this question several years ago and, since then, my knowledge of this topic has improved.
First of all, I mentioned I wanted random numbers for a login system. Login systems are security mechanisms.
This means that any random number generators that the login system relies on should be cryptographically secure.
PHP's rand and mt_rand are not cryptographically secure.
In these cases, it's best to be safe than sorry. There are random number generators designed specifically to be secure, notably openssl_random_pseudo_bytes (which is unfortunately not always available -- you must enable the OpenSSL extension for it to work). On *NIX systems (such as Linux), bytes read from /dev/urandom can be used as well.
Unfortunately (for the purposes of this question), both of these approaches return binary data instead of hexadecimal. Fortunately, PHP already has a function to fix this for us, bin2hex, which works for strings of any length.
So here's how the code would look like:
function generate_secure_random_hex_string($length) {
// $length should be an even, non-negative number.
// Because each byte is represented as two hex digits, we'll need the binary
// string to be half as long as the hex string.
$binary_length = $length / 2;
// First, we'll generate the random binary string.
$random_result = openssl_random_pseudo_bytes($binary_length, $cstrong);
if (!$cstrong) {
// The result is not cryptographically secure. Abort.
// die() is just a placeholder.
// There might be better ways to handle this error.
die();
}
//Convert the result to hexadecimal
return bin2hex($random_result);
}
// Example:
echo generate_secure_random_hex_string(32);
I've often seen this handled in login systems by just doing something like:
$salt = "big string of random stuff"; // you can generate this once like above
$token = md5( $salt . time()); // this will be your "unique" number
MD5 hashes can have collisions, but this is pretty effective and very simple.
As of PHP 5.3:
function getRandomHex($num_bytes=4) {
return bin2hex(openssl_random_pseudo_bytes($num_bytes));
}
For your example of 128 bits:
$rand128 = getRandomHex(16);
I want to create a token generator that generates tokens that cannot be guessed by the user and that are still unique (to be used for password resets and confirmation codes).
I often see this code; does it make sense?
md5(uniqid(rand(), true));
According to a comment uniqid($prefix, $moreEntopy = true) yields
first 8 hex chars = Unixtime, last 5 hex chars = microseconds.
I don't know how the $prefix-parameter is handled..
So if you don't set the $moreEntopy flag to true, it gives a predictable outcome.
QUESTION: But if we use uniqid with $moreEntopy, what does hashing it with md5 buy us? Is it better than:
md5(mt_rand())
edit1: I will store this token in an database column with a unique index, so I will detect columns. Might be of interest/
rand() is a security hazard and should never be used to generate a security token: rand() vs mt_rand() (Look at the "static" like images). But neither of these methods of generating random numbers is cryptographically secure. To generate secure secerts an application will needs to access a CSPRNG provided by the platform, operating system or hardware module.
In a web application a good source for secure secrets is non-blocking access to an entropy pool such as /dev/urandom. As of PHP 5.3, PHP applications can use openssl_random_pseudo_bytes(), and the Openssl library will choose the best entropy source based on your operating system, under Linux this means the application will use /dev/urandom. This code snip from Scott is pretty good:
function crypto_rand_secure($min, $max) {
$range = $max - $min;
if ($range < 0) return $min; // not so random...
$log = log($range, 2);
$bytes = (int) ($log / 8) + 1; // length in bytes
$bits = (int) $log + 1; // length in bits
$filter = (int) (1 << $bits) - 1; // set all lower bits to 1
do {
$rnd = hexdec(bin2hex(openssl_random_pseudo_bytes($bytes)));
$rnd = $rnd & $filter; // discard irrelevant bits
} while ($rnd >= $range);
return $min + $rnd;
}
function getToken($length=32){
$token = "";
$codeAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$codeAlphabet.= "abcdefghijklmnopqrstuvwxyz";
$codeAlphabet.= "0123456789";
for($i=0;$i<$length;$i++){
$token .= $codeAlphabet[crypto_rand_secure(0,strlen($codeAlphabet))];
}
return $token;
}
This is a copy of another question I found that was asked a few months before this one. Here is a link to the question and my answer: https://stackoverflow.com/a/13733588/1698153.
I do not agree with the accepted answer. According to PHPs own website "[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()."
I do not think the answer could be clearer than this, uniqid is not secure.
I know the question is old, but it shows up in Google, so...
As others said, rand(), mt_rand() or uniqid() will not guarantee you uniqueness... even openssl_random_pseudo_bytes() should not be used, since it uses deprecated features of OpenSSL.
What you should use to generate random hash (same as md5) is random_bytes() (introduced in PHP7). To generate hash with same length as MD5:
bin2hex(random_bytes(16));
If you are using PHP 5.x you can get this function by including random_compat library.
Define "unique". If you mean that two tokens cannot have the same value, then hashing isn't enough - it should be backed with a uniqueness test. The fact that you supply the hash algorithm with unique inputs does not guarantee unique outputs.
To answer your question, the problem is you can't have a generator that is guaranteed random and unique as random by itself, i.e., md5(mt_rand()) can lead to duplicates. What you want is "random appearing" unique values. uniqid gives the unique id, rand() affixes a random number making it even harder to guess, md5 masks the result to make it yet even harder to guess. Nothing is unguessable. We just need to make it so hard that they wouldn't even want to try.
I ran into an interesting idea a couple of years ago.
Storing two hash values in the datebase, one generated with md5($a) and the other with sha($a). Then chek if both the values are corect. Point is, if the attacker broke your md5(), he cannot break your md5 AND sha in the near future.
Problem is: how can that concept be used with the token generating needed for your problem?
First, the scope of this kind of procedure is to create a key/hash/code, that will be unique for one given database. It is impossible to create something unique for the whole world at a given moment.
That being said, you should create a plain, visible string, using a custom alphabet, and checking the created code against your database (table).
If that string is unique, then you apply a md5() to it and that can't be guessed by anyone or any script.
I know that if you dig deep into the theory of cryptographic generation you can find a lot of explanation about this kind of code generation, but when you put it to real usage it's really not that complicated.
Here's the code I use to generate a simple 10 digit unique code.
$alphabet = "aA1!bB2#cC3#dD5%eE6^fF7&gG8*hH9(iI0)jJ4-kK=+lL[mM]nN{oO}pP\qQ/rR,sS.tT?uUvV>xX~yY|zZ`wW$";
$code = '';
$alplhaLenght = strlen($alphabet )-1;
for ($i = 1; $i <= 10; $i++) {
$n = rand(1, $alplhaLenght );
$code .= $alphabet [$n];
}
And here are some generated codes, although you can run it yourself to see it work:
SpQ0T0tyO%
Uwn[MU][.
D|[ROt+Cd#
O6I|w38TRe
Of course, there can be a lot of "improvements" that can be applied to it, to make it more "complicated", but if you apply a md5() to this, it'll become, let's say "unguessable" . :)
MD5 is a decent algorithm for producing data dependent IDs. But in case you have more than one item which has the same bitstream (content), you will be producing two similar MD5 "ids".
So if you are just applying it to a rand() function, which is guaranteed not to create the same number twice, you are quite safe.
But for a stronger distribution of keys, I'd personally use SHA1 or SHAx etc'... but you will still have the problem of similar data leads to similar keys.