PHP 7 OAuthProvider VS random_bytes token generation - php

My goal is simply to generate a temporary token that will be used in URLs for user identification, should I use OAuthProvider::generateToken or random_bytes?
From these answers :
Generate a single use token in PHP: random_bytes or openssl_random_pseudo_bytes?
and
best practice to generate random token for forgot password
It seems that random_bytes is a more recently updated option for PHP 7 when compared to openssl_random_pseudo_bytes. Is it the same when compared to OAuthProvider::generateToken?
Examples:
$rb_token = bin2hex(random_bytes($length));
$oa_token = bin2hex((new OAuthProvider())->generateToken($length, TRUE));
// TRUE = strong "/dev/random will be used for entropy"

I would go for random_bytes (if you use PHP < 7, a polyfill with a good reputations exists, random_compat).
Differences in between /dev/urandom and /dev/random are negligible for your case (a more detailed explanation can be found there).
random_bytes use getrandom syscall or /dev/urandom, (source code), while OAuthProvider::generateToken() without strong mode seems to fallback to some unsecure implementation if the generated string is too short (source code, and source code of php_mt_rand). So random_bytes has a edge over usability (no unsafe mode), and on systems using getrandom syscall, as it should be the preferred method (detailled explanation here).
Another way to conclude is to look at some widely-used libraries: FOSOAuthServer bundle, for Symfony applications, uses random_bytes too.
Last but not least, I believe some PHP core function will also receive more scrutinity, and better support, than an extension.

Related

PHP equivalent to Rijndael AES encryption/decryption in .Net?

Is there an equivalent functions in PHP that will allow interoperability with the .Net Rijndael AES encryption/decryption? (The encryption .Net code is below).
Basically, if I encrypt in .Net can I decrypt in PHP and vice-versa?
string outStr = null; // Encrypted string to return
RijndaelManaged aesAlg = null; // RijndaelManaged object used to encrypt the data.
// Generate the key from the shared secret and the salt.
Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(sharedSecret, _salt);
// Create a RijndaelManaged object
aesAlg = new RijndaelManaged();
aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);
// Create a decryptor to perform the stream transform.
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
// prepend the IV
msEncrypt.Write(BitConverter.GetBytes(aesAlg.IV.Length), 0, sizeof(int));
msEncrypt.Write(aesAlg.IV, 0, aesAlg.IV.Length);
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(plainText);
}
}
outStr = Convert.ToBase64String(msEncrypt.ToArray());
}
You can, maybe, but I cannot in good conscience recommend it.
PHP offers two extensions that can, in principle, do the job. OpenSSL suffers from not being documented beyond function prototypes, and Mcrypt suffers from being an absolute minefield if you don't happen to know exactly what you're doing. I wouldn't use either if I could possibly get away with it.
If you do attempt this, you will need to implement authentication yourself. You will need to implement padding yourself. If you screw up, you will get no indication even if the library knows perfectly well it's been asked to do something absurd, for the most part it will (silently!) guess at what you meant and continue on (patches for much of this are available, but not yet in mainline).
Godspeed.
As long as PHP and .Net follow specs encryption/decryption should work. You may check this topic for more info and examples Using PHP mcrypt with Rijndael/AES
As #Andrew says, once you get everything in the same spec, it should work. AES is quite a well used algorithm so librarys in both languages should match up. Any problems are usually to do with the password to key derivation functions in the different languages. This project tries to solve the intercommunication issues between .NET and PHP. It implements Rfc2898DeriveBytes() in PHP using a pbkdf2 with 1000 iterations of HMACSHA1 which I guess is what .NET uses by default.
It is a rather simple AES string encryption so there is no authentication. The padding issue is solved by using base64 encoding and rtrimming null characters on decryption so it is NOT a binary safe implementation. I take no credit or responsibility for this code, nor have I tested it between the different environments, but I feel it could help someone fill in the gaps.

Using bcrypt-ruby to validate hashed passwords using version $2y

We're in a bit of a bind where we need to use Ruby to auth users against an existing db of users. The user's passwords were all generated using password_compat PHP library. All the hashed passwords start with $2y.
I've been using bcrypt-ruby to try and authenticate the users and I haven't found any success.
#This user's password is "password"
irb(main):041:0> g = BCrypt::Password.new("$2y$10$jD.PlMQwFSYSdu4imy8oCOdqKFq/FDlW./x9cMxoUmcLgdvKCDNd6")
=> "$2y$10$jD.PlMQwFSYSdu4imy8oCOdqKFq/FDlW./x9cMxoUmcLgdvKCDNd6"
irb(main):042:0> g == "password"
=> false
irb(main):044:0> g.version
=> "2y"
irb(main):045:0> g.cost
=> 10
irb(main):046:0> g.salt
=> "$2y$10$jD.PlMQwFSYSdu4imy8oCO"
irb(main):047:0> g.hash
=> -219334950017117414
I'm not very experienced with bcrypt or encryption in general. Can bcrypt-ruby handle $2y? I looked through the source and I don't think it can. Is this the fault of the underlying OS (I'm using OS X)?
Yes, bcrypt-ruby can handle passwords hashed with 2y. You just need to replace the 2y by 2a:
irb(main):002:0> BCrypt::Password.new("$2a$10$jD.PlMQwFSYSdu4imy8oCOdqKFq/FDlW./x9cMxoUmcLgdvKCDNd6") == "password"
=> true
This is necessary as bcrypt-ruby seems to follow Solar Designer’s first suggestion to introduce just 2x for a backward-compatible support for the “sign extension bug”:
[…] I am considering keeping support
for the broken hashes under another prefix - say, "$2x$" (where the "x"
would stand for "sign eXtension bug") instead of the usual "$2a$".
Later he proposed to also introduce the 2y prefix for a better distinction between the three versions:
One idea is to allocate yet another prefix, which will mean the same
thing as 2a, but "certified" as passing a certain specific test suite
(which will include 8-bit chars). So we'll have:
2a - unknown correctness (may be correct, may be buggy)
2x - sign extension bug
2y - definitely correct
Newly set/changed passwords will be getting the new prefix.
PHP supports 2a, 2x, and 2y while bcrypt-ruby supports only 2a, and 2x. But if you know your implementation doesn’t have the “sign extension bug”, you can just replace 2y by 2a, as 2y means the same thing as 2a.

Gathering entropy in web apps to create (more) secure random numbers

after several days of research and discussion i came up with this method to gather entropy from visitors (u can see the history of my research here)
when a user visits i run this code:
$entropy=sha1(microtime().$pepper.$_SERVER['REMOTE_ADDR'].$_SERVER['REMOTE_PORT'].
$_SERVER['HTTP_USER_AGENT'].serialize($_POST).serialize($_GET).serialize($_COOKIE));
note: pepper is a per site/setup random string set by hand.
then i execute the following (My)SQL query:
$query="update `crypto` set `value`=sha1(concat(`value`, '$entropy')) where name='entropy'";
that means we combine the entropy of the visitor's request with the others' gathered already.
that's all.
then when we want to generate random numbers we combine the gathered entropy with the output:
$query="select `value` from `crypto` where `name`='entropy'";
//...
extract(unpack('Nrandom', pack('H*', sha1(mt_rand(0, 0x7FFFFFFF).$entropy.microtime()))));
note: the last line is a part of a modified version of the crypt_rand function of the phpseclib.
please tell me your opinion about the scheme and other ideas/info regarding entropy gathering/random number generation.
ps: i know about randomness sources like /dev/urandom.
this system is just an auxiliary system or (when we don't have (access to) these sources) a fallback scheme.
In the best scenario, your biggest danger is a local user disclosure of information exploit. In the worst scenario, the whole world can predict your data. Any user that has access to the same resources you do: the same log files, the same network devices, the same border gateway, or the same line that runs between you and your remote connections allows them to sniff your traffic by unwinding your random number generator.
How would they do it? Why, basic application of information theory and a bit of knowledge of cryptography, of course!
You don't have a wrong idea, though! Seeding your PRNG with real sources of randomness is generally quite useful to prevent the above attacks from happening. For example, this same level of attack can be exploited by someone that understands how /dev/random gets populated on a per-system basis if the system has low entropy or its sources of randomness are reproducible.
If you can sufficiently secure the processes that seed your pool of entropy (for example, by gathering data from multiple sources over secure lines), the likelihood that someone is able to listen in becomes smaller and smaller as you get closer and closer to the desirable cryptographic qualities of a one-time pad.
In other words, don't do this in PHP, using a single source of randomness fed into a single Mersenne twister. Do it properly, by reading from your best, system-specific alternative to /dev/random, seeding its entropy pool from as many secure, distinct sources of "true" randomness as possible. I understand you've stated that these sources of randomness are inaccessible, but this notion is strange when similar functions are afforded to all major operating systems. So, I suppose I find the concept of an "auxiliary system" in this context to be dubious.
This will still be vulnerable to an attack by a local user cognizant of your sources of entropy, but securing the machine and increasing the true entropy within /dev/random will make it far more difficult for them to do their dirty work short of a man-in-the-middle attack.
As for cases where /dev/random is indeed accessible, you can seed it fairly easily:
Look at what options exist on your system for using /dev/hw_random
Embrace rngd (or a good alternative) for defining your sources of randomness
Use rng-tools for inspecting and improving your randomness profile
And finally, if you need a good, strong source of randomness, consider investing in more specialized hardware.
Best of luck in securing your application.
PS: You may want to give questions like this a spin at Security.SE and Cryptography.SE in the future!
Use Random.Org
If you need truly random numbers, use random.org. These numbers are generated via atmospheric noise. Besides library for PHP, it also has a http interface which allows you to get truly random numbers by simple requests:
https://www.random.org/integers/?num=10&min=1&max=6&col=1&base=10&format=plain&rnd=new
This means that you can simply retrieve the real random numbers in PHP without any additional PECL exension on the server.
If you don't like other users to be able to "steal" your random numbers (as MrGomez' argues), just use https with a certificate checking. Here follows an example with https certificate checking:
$url = "https://www.random.org/integers/?num=10&min=1&max=6&col=1&base=10&format=plain&rnd=new";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
$response = curl_exec($ch);
if ($response === FALSE)
echo "http request failed: " . curl_error($ch);
else
echo $response;
curl_close($ch);
If you need more information on how to create https requests:
Make a HTTPS request through PHP and get response
http://unitstep.net/blog/2009/05/05/using-curl-in-php-to-access-https-ssltls-protected-sites/
More on security
Again, some might argue that if the attacker queries random.org at the same time as you, he might get the same numbers and predict.. I don't know if random.org would even work this way, but if you are really concerned, you may lessen the chance by fooling the attacker with dummy request which you throw out, or use only a certain part of the random numbers you get.
As MrGomez notes in his comment, this shall not be considered as an ultimate solution to security, but only as one of possible sources of entropy.
Performance
Of course, if you need a blitz latency then doing one random.org request per one client request might not be best idea... but what about just doing one bigger request to pre-cache the random numbers like every 5 minutes?
To come to the point, as far as i know there is no way to generate entrophy inside a PHP script, sorry for this non-answer. Even if you look at well etablished scripts like phppass, you will see, that their fallback system cannot do some magic.
The question is, whether you should try it anyway or not. Since you want to publish your system under GPL, you propably don't know in what scenario it will be used. In my opinion it's best then to require a random source, or to fail fast (die with an appropriate error message), so a developer who wants to use your system, knows immediately, that there is a problem.
To read from the random source, you could call the mcrypt_create_iv() function...
$randomBinaryString = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
...this function reads from the random pool of the operating system. Since PHP 5.3 it does it on Windows servers as well, so you can leave it to PHP to handle the random source.
If you have access to /dev/urandom you can use this:
function getRandData($length = 1024) {
$randf = fopen('/dev/urandom', 'r');
$data = fread($randf, $length);
fclose($randf);
return $data;
}
UPDATE:
of course you should have some backup in case opening the device fails
should you have access to client side, you can enable mouse movement tracking - this is what true crypt is using for extra level of entropy.
as i have said before, my rand function is a modified version of phpseclib's crypt_random function.
u could see it in the link given on my first post. at least the author of the phpseclib cryptographic library confirmed it; not enough for ordinary apps? i don't speak of extreme/theoretical security, just speak about practical security to the extent really needed and at the same time 'easily'/'sufficiently low cost' available for almost all of the ordinary applications on the web.
phpseclib's crypt_random effectively and silently falls back to the mt_rand (which u should know is really weak) in the worst case (no openssl_random_pseudo_bytes or urandom available), but my function uses a much more secure scheme in such cases. it's just a fall back to a scheme that brute-forcing/predicting its output is much harder and (should be) in practice sufficient for all ordinary apps/sites. it uses possible (in practice very likely and hard to predict/circumvent) extra entropy that is gathered over time which quickly becomes almost impossible to know for outsiders. it adds this possible entropy to the mt_rand's output (and also to the output of other sources: urandom, openssl_random_pseudo_bytes, mcrypt_create_iv). if u are informed u should know, this entropy can be added but not subtracted. in the (almost surely really rare) worst case, that extra entropy would be 0 or some too tiny amount. in the mediocre case, which i think is almost all of the cases, it would be even more than practically necessary, i think. (i have had vast cryptography studies, so when i say i think, it is based on a much more informed and scientific analysis than ordinary programmers).
see the full code of my modified crypt_random:
function crypt_random($min = 0, $max = 0x7FFFFFFF)
{
if ($min == $max) {
return $min;
}
global $entropy;
if (function_exists('openssl_random_pseudo_bytes')) {
// openssl_random_pseudo_bytes() is slow on windows per the following:
// http://stackoverflow.com/questions/1940168/openssl-random-pseudo-bytes-is-slow-php
if ((PHP_OS & "\xDF\xDF\xDF") !== 'WIN') { // PHP_OS & "\xDF\xDF\xDF" == strtoupper(substr(PHP_OS, 0, 3)), but a lot faster
extract(unpack('Nrandom', pack('H*', sha1(openssl_random_pseudo_bytes(4).$entropy.microtime()))));
return abs($random) % ($max - $min) + $min;
}
}
// see http://en.wikipedia.org/wiki//dev/random
static $urandom = true;
if ($urandom === true) {
// Warning's will be output unles the error suppression operator is used. Errors such as
// "open_basedir restriction in effect", "Permission denied", "No such file or directory", etc.
$urandom = #fopen('/dev/urandom', 'rb');
}
if (!is_bool($urandom)) {
extract(unpack('Nrandom', pack('H*', sha1(fread($urandom, 4).$entropy.microtime()))));
// say $min = 0 and $max = 3. if we didn't do abs() then we could have stuff like this:
// -4 % 3 + 0 = -1, even though -1 < $min
return abs($random) % ($max - $min) + $min;
}
if(function_exists('mcrypt_create_iv') and version_compare(PHP_VERSION, '5.3.0', '>=')) {
#$tmp16=mcrypt_create_iv(4, MCRYPT_DEV_URANDOM);
if($tmp16!==false) {
extract(unpack('Nrandom', pack('H*', sha1($tmp16.$entropy.microtime()))));
return abs($random) % ($max - $min) + $min;
}
}
/* Prior to PHP 4.2.0, mt_srand() had to be called before mt_rand() could be called.
Prior to PHP 5.2.6, mt_rand()'s automatic seeding was subpar, as elaborated here:
http://www.suspekt.org/2008/08/17/mt_srand-and-not-so-random-numbers/
The seeding routine is pretty much ripped from PHP's own internal GENERATE_SEED() macro:
http://svn.php.net/viewvc/php/php-src/tags/php_5_3_2/ext/standard/php_rand.h?view=markup */
static $seeded;
if (!isset($seeded) and version_compare(PHP_VERSION, '5.2.5', '<=')) {
$seeded = true;
mt_srand(fmod(time() * getmypid(), 0x7FFFFFFF) ^ fmod(1000000 * lcg_value(), 0x7FFFFFFF));
}
extract(unpack('Nrandom', pack('H*', sha1(mt_rand(0, 0x7FFFFFFF).$entropy.microtime()))));
return abs($random) % ($max - $min) + $min;
}
$entropy contains my extra entropy which comes from all requests parameters' entropy combined till now + current request's parameters entropy + the entropy of a random string (*) set by hand at the installation time.
*: length: 22, composed of lower and uppercase letters + numbers (more than 128 bits of entropy)
Update 2: Code Review Warning to Everyone: Dont use The code in the original question. It's a security liability. If this code is online anywhere Remove it as it open the whole system, network and database to a malevolent user. Your not only exposing your code but all of your users data.
Do not ever Serialize user inputs. If in your code your already doing it, Stop your server and change your code. This is a great exemple of Not doing crypto by yourself.
Update 1: For real security you need to have UN-guessable randomess in your entropy. A suitable option to add entropy has your Question refer-to is to use the Delta of your script's execution time Not microtime() by itself . Because the Delta Rely on the load of your server. And so is a combination of the hardware environment, temperature, network load, power load, disk access, Cpu usage and voltage fluctuation which together are unpredictable.
Using Time(), timestamp or microtime is a flaw in your implementation.
Script execution Delta Exemple code coming:
#martinstoeckli stated correctly that a Suitable Random generation for crypto is from
mcrypt_create_iv($lengthinbytes, MCRYPT_DEV_URANDOM);
but is outside the requirements of not having a crypto module
In SQL use the RAND() in conjunction with your generated number.
http://www.tutorialspoint.com/mysql/mysql-rand-function.htm
Php offer as well the Rand() function
http://php.net/manual/en/function.rand.php
they wont give you the same number so you could use both.
rn_rand() should be getting used not rand()

Does Security:cipher encrypted result depend on the server?

I'm working in a cakephp application where I use Security::cipher in order to encrypt some data. It works perfectly but I've moved files and DB to another server and now the encrypted result is different.
I've tried with some simple lines:
$security = new Security;
$code = $security->cipher('1234', Configure::read('Security.cipherSeed'));
When I print $code, the value is different in both servers. I've configured the same Security.cipherSeed in both core.php files.
Is Security::cipher function using some server value to encrypt?
Thank you.
Well, looking at this bug, it does appear to be an issue.
Digging into the source code, this line is what makes it work:
srand(Configure::read('Security.cipherSeed'));
Now, why does that work? Because rand() implements a pseudo-random algorithm. So for any given known seed, you can theoretically produce the same series of random output. To see if this will work, let's look at the PHP source code for rand(), specifically the internal php_rand function:
PHPAPI long php_rand(TSRMLS_D)
{
long ret;
if (!BG(rand_is_seeded)) {
php_srand(GENERATE_SEED() TSRMLS_CC);
}
We know this isn't the problem, since we're manually seeding (unless we have the suhosin patch installed on the server, then it will always reseed and hence not work).
#ifdef ZTS
ret = php_rand_r(&BG(rand_seed));
#else
# if defined(HAVE_RANDOM)
ret = random();
# elif defined(HAVE_LRAND48)
ret = lrand48();
# else
ret = rand();
# endif
#endif
Woah, did you see what happened? Depending on the server specification, is can use one of 4 different random libraries (rand(), random(), lrand48() or it's own internal random function php_rand_r)! That's why it's not portable across server installs.
Instead, use a real encryption library such as MCrypt or GPG.
Edit: I've submitted a bug report on this topic to cake.

Use of undefined constant CRYPT_SHA512

I use a php script that hashes passwords using php's crypt and uses SHA512, however when I try to check if SHA512 is set I get the above error. Of course I know WHY I get this error.. php is missing some dependency. I just don't know what that dependency is.
Can anyone please tell me what I need to install (on a Ubuntu server) to be able to use SHA512 in PHP ?
Thanks!
The php docs say that built-in support for SHA-256 and SHA-512 was added in PHP 5.3.2. If you use any earlier versions of PHP, it relies on implementations of those algorithms provided by your system, which apparently yours does not have.
Make sure you have newest version of PHP5 and install mcrypt - also contains alot of other encryption methods like rinjdael (AES)
Bottom line, you should be using mcrypt, not SHA512 or any md5 style hasher. It's too easy to brute force decrypt them. There is a ton of good reference at http://www.php.net/manual/en/function.mcrypt-generic.php and all over google. Below is an example of a 3DES hashed URL using the reference function urlsafe_b64encode from http://www.php.net/manual/en/function.mcrypt-generic.php#71135
$key = "what can i tell you";
$request = http_build_query($_REQUEST);
$request_enc = urlencode(urlsafe_b64encode(mcrypt_ecb(MCRYPT_3DES, $key, $request, MCRYPT_ENCRYPT)));
$url = "http://localhost/takemerightthere/".$request_enc;

Categories