using rand() for CSRF, can it be safe(r)? - php

I have this function for CSRF protection, it is pretty insane.
function GenToken($ranLen) {
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!##$()';
$randomString = '';
for ($i = 0; $i < $ranLen; $i++) {
$randomString .= $characters[rand(0, strlen($characters) - 1)];
}
return $randomString;
}
It is called up by this:
$token = GenToken(rand(32,128));
It uses PHP's rand() which I know is far from ideal when it comes to creating random numbers.
What I am wondering is just how bad is it? Is this function suitable for 'good' (granted wacky) CSRF protection? It sure as hell generates one heck of a string.
Currently the function is only used for CSRF however it could be used for other short random strings like a code emailed to the user to activate their account ect. Is this acceptable?

It'll probably be good enough, as long as the generated tokens are user-specific and/or are expired relatively soon. However, if you're going to change it at all, you should change it to use a decent PRNG, which is available on most systems in the form of /dev/random and can be accessed using a number of ways:
mcrypt_create_iv($raw_salt_len, MCRYPT_DEV_URANDOM)
openssl_random_pseudo_bytes($raw_salt_len)
fopen('/dev/urandom', 'r') // then fread enough bytes from it
Simply bin2hex or base64_encode the return values of the above. Your rand (or better mt_rand) solution should only be a fallback in case none of the above are available.

For the similar task I'd prefer to use built-in php function called uniqid http://www.php.net/manual/en/function.uniqid.php
It should be faster and safer then your implementation

Related

PHP Random string generation miraculously generated the same string

So I've got a fairly simple function in PHP that renders 10 character long order IDs:
function createReference($length = 10)
{
$characters = 'ABCDEFGHIJKLMNPQRSTUVWXYZ123456789';
$string = '';
for ($i = 0; $i < $length; $i++) {
$string .= $characters[rand(0, strlen($characters) - 1)];
}
return $string;
}
However, today on the 154020th table record, it generated the same 10-character ID as a previous order ID (which was the 144258th record in the table), and tried to insert it. Since I have a UNIQUE restriction on the column, I got an error and I received a notification from this.
According to my calculations, the script above creates 34^10 = 2.064.377.754.059.776 different possibilities.
I've read some stuff about rand() and mt_rand() doing different stuff but that shouldnt be an issue on PHP 7.1+. The script is running on PHP 7.3.
So should I buy a lottery ticket right now, or is there something predictable about the pseudo-randomness being used here? If so, what is a solution to have better distribution?
Assuming rand() is a true RNG, then the expected chance to generate a duplicate reaches 50% after reaching a little more than the square root of all possibilities (see "Birthday problem" for a more precise statement and formulas). The square root of 34^10 is 45435424, so it's well over 144258, but of course, rand() is far from being a perfect or "true" RNG.
In any case, generating a unique random identifier using rand or mt_rand (rather than a cryptographic RNG such as random_int) is a bad idea anyway. Depending on whether or not IDs have to be hard to guess, or whether or not the ID alone is enough to grant access to the resource, it may or may not be a better idea to use auto-incrementing record numbers rather than random numbers. See my section "Unique Random Identifiers" for further considerations.
See also this question.

When does mod_php in apache seed the random number generator?

I am using the php rand() function to generate coupon codes for my e commerce system.
It worked fine for a while but now I am getting a lot of errors that the code is already in the system.
This is the function I use:
function generateRandomString($length) {
$characters = '0123456789abcdefghijklmnopqrstuvwxyz';
$randomString = '';
for ($i = 0; $i < $length; $i++) {
$randomString .= $characters[rand(0, strlen($characters) - 1)];
}
return $randomString;
}
And my codes are 32 characters long.
I did a sample of ~150 tries and noticed that more than 50% of the generated codes where laready in the system.
I have 4212 codes in the system. The odds of a 32 character random string with 36 different symbols producing a collision are basically zero, and I get 50% collisions.
When I re-seeded the random number generator in my function by calling srand(); I did not have any collisions any more.
But on the man page of php it cleary says:
Note: As of PHP 4.2.0, there is no need to seed the random number
generator with srand() or mt_srand() as this is now done
automatically.
I am running php version PHP 5.5.9
So my thoughts where something like that seeding is done, but only once per webserver worker, and then when the process is forked, it is not reseeded or something like that.
But that would be clearly a bug in apache...
I am running php as apache modul in apache version Apache/2.4.7 (Ubuntu) and the mpm_prefork_module module
So do I still need to call srand() at the top of every script dispite the manpages saying other wise, and why? Is it apaches fault or PHP's?
And yes, I am aware that I should not use this function for this purpose, and I will update it to use cryptographically secure numbers. But I think this should not happen anyway and I am still interested in what is going on!
If your codes are 32 characters long, then why don't you simply encrypt the current microtime with md5 ?
$coupon = md5( microtime() );
One line simple. And if you want a touch of randomness, just throw a
$coupon = md5( microtime() . mt_rand( 0, 10000) );
On there like a salt. That will almost guarantee you will never duplicate. As for the why it is not as random.
PHP’s random number generators are seeded only once per process.
See this posting ...
http://phpsecurity.readthedocs.org/en/latest/Insufficient-Entropy-For-Random-Values.html
by the way I don't think you need cryptographically secure hashes, only sufficiently random ones that cant be easily guessed. Even with a cryptographic hash, users will enter said hash into the cart for the coupon, it's a simple matter to brute force even a cryptographically secure hash then, you'd do better to invest time in only allowing "n" attempts, or "n" attempts per second etc. To reduce the rate a brute force attack can be done.
For example, I would just try all combinations of 32 character hashes. So it doesn't matter in the end, because you are not using plaintext entries like a password, and then hiding your salting and encryption method. The number of coupons active would determine my success rate and the time it takes me in either case ... If you follow.
IMPLIED IN MY ANSWER IS THIS
PHP’s random number generators are seeded only once per process. Forking does not create a new process but copies the current processes state.
See
Calling rand/mt_rand on forked children yields identical results
and
http://wiki.openssl.org/index.php/Random_fork-safety
and
http://www.reddit.com/r/shittyprogramming/comments/2jvzgq/sometimes_it_takes_real_shitty_code_to_expose_an/
Additionally this is not an issue specific to php but more so to psudorandom number generation in general.
See this: https://github.com/php/php-src/blob/d0cb715373c3fbe9dc095378ec5ed8c71f799f67/ext/standard/rand.c#L66-L68
Apparently RNG is being reseeded on first call to rand() (or explicitly calling srand()).
Since fork copies parent's memory, child also gets parent's seed - never getting reseeded.
function generateRandomString($length) {
$characters = '0123456789abcdefghijklmnopqrstuvwxyz';
$randomString = '';
for ($i = 0; $i < $length; $i++) {
$randomString .= $characters[rand(0, strlen($characters) - 1)];
}
return substr(time().$randomString,0,$length);
}

How to go about encrypting user login passwords for a website

I'm developing a website for a company, and they require login verification in order to use some of the services that this company provides. Using common sense, I know that I need to encrypt the user's password when saving it to a database. Not a problem, I can simply implement a hashing algorithm and store the hash (or something, I still need to troll StackOverflow to figure out the best way to save this information, but that's not what I'm asking).
What I'm curious about is how to actually execute the encryption algorithm. Is that a stand-alone program on the server that will encrypt the password and then store it? Or would I have to use a PHP module to encrypt the password? Or is it something else that I'm not thinking of?
Any and all answers are appreciated, and if I worded anything poorly, I'm counting on you to call me out on it ;)
PHP's built in hashing methods works well. No need to call inn third party libraries.
You should take a look at the hash() function. Remember to use a strong salt when saving the hash. There should be plenty of good articles around the internet about this, and also here.
As pointed out by drrcknlsn in a comment, md5 and sha1 would bad choices since they are considered broken.
Also, as Grexis points out in his answer, PBKDF2 is also one method the you could look into.
I realize that this question has already been answered, but here is a hashing function that I use. It's a PHP PBKDF2 Implementation (described in RFC 2898):
public static function hash($p, $s, $c = 5000, $kl = null, $a = 'sha256'){
$hl = strlen(hash($a, null, true));
if(is_null($kl)) $kl = $hl;
$kb = ceil($kl/$hl);
$dk = '';
for($block = 1; $block <= $kb; $block++){
$ib = $b = hash_hmac($a, $s.pack('N', $block), $p, true);
for($i = 0; $i < $c; $i++)
$ib ^= ($b = hash_hmac($a, $b, $p, true));
$dk .= $ib;
}
return substr($dk, 0, $kl);
}
More information here: Encrypting Passwords with PHP (The function above is only slightly modified from this location to provide default values)

OS-independent random byte sequence in PHP

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.

md5(uniqid) makes sense for random unique tokens?

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.

Categories