where hash() function is used and why - php

The function in php string hash ( string $algo , string $data [, bool $raw_output = false ] ) where algo=Name of selected hashing algorithm (i.e. "md5", "sha256", "haval160,4", etc..), data=Message to be hashed., raw_output=When set to TRUE, outputs raw binary data. FALSE outputs lowercase hexits. so if I have this example
<?php
echo hash('ripemd128', 'The quick brown fox jumped over the lazy dog.');
?>
The above example output (which looks completely random): 51d43720fd516108ef5ed20e9031bb865ede861e
So I'm wondering where such functions is used and why? also Is there a way or a function to revert the output to the original string back again?

So I'm wondering where such functions is used and why?
They're used in digital signature algorithms, digital fingerprinting algorithms, content-addressable storage, constructing tamper-resistant data structures that can be traversed rapidly and securely, and for fast lookups that also resist complexity attacks.
also Is there a way or a function to revert the output to the original string back again?
No. In many cases, having this ability would defeat the point of the hash. Also, it is trivial to prove that this is, as stated, impossible, using a counting argument.

It is use for digital signatures like hashing the concatenated string with the secret key against the other to check if both hash string is correct, its like a key in order for you to gain access to do something.
There's no way to decrypt it because that is how it's made, it is a 1-way hash method.
if you want a method that encrypts and decrypts the string use mcrypt_encrypt and mcrypt_decrypt

this functions are used to compute a kind of "fingerprint" for some data. in your example this will be your string. same algorithm will produce the same hash for the same input data. if you change input data the computed hash will be different.
a popular usage is storing passwords. so you don't store passwords in clear text but hashed values.
for the second part of your question: hash algorithms are "one-way" only (should be ;)). so you can not restore the input data from hashed value.

Related

esp32 and php XXTEA strings encryption

I'm using esp32 (Arduino platform not esp-idf) with the "HTTPClient.h" library to send get requests with parameters to my PHP server.
I want to encrypt the parameter values and decrypt them in my PHP code And vice versa (my server sends back JSON data to my esp32).
I tried using the XXTEA protocol with these libraries for PHP, and for esp32.
But the encrypted string won't decrypt properly on PHP.
Example:
When I encrypt "HELLO WORLD" on my esp32 with the key "ENCRYPTION KEY" I get this:
35bd3126715874f741518f4d
And when I decrypt it on PHP it returns blank.
Moreover, when I encrypt it on my PHP server I get this:
T1YNYC4P4R2Y5eCxUqtjuw==
My esp32 sketch looks like this:
#include <xxtea-iot-crypt.h>
void setup() {
Serial.begin(115200);
}
void loop() {
String plaintext = "HELLO WORLD";
// Set the Password
xxtea.setKey("ENCRYPTION KEY");
// Perform Encryption on the Data
Serial.print(F(" Encrypted Data: "));
String result = xxtea.encrypt(plaintext);
Serial.println(result);
// Perform Decryption
Serial.print(F(" Decrypted Data: "));
Serial.println(xxtea.decrypt(result));
delay(2000);
}
My PHP code looks like this:
require_once('xxtea.php');
$str = "HELLO WORLD"
$key = "ENCRYPTION KEY";
$encrypt_data = xxtea_encrypt($str, $key);
error_log($encrypt_data);
Is there a way to have an encrypted strings communication between PHP and esp32?
Thanks in advance.
This problem may result from inputs being of different data type, since no current XXTEA implementation seems to do any type or range checking.
Or it could be due to different endian behavior of the two computers involved, since binary is typically stored as an array of words constructed from bytes.
Or it could be due to lack of official or standard reference examples for correct encryption of a specific string and key. In the absence of reference examples (using either hexadecimal or base64 conversion of the binary encryption result) there is no way to tell whether an implementation of encryption is correct, even if its results decrypt correctly using a corresponding decryption implementation.
ADDED:
I think I've found one compatibility problem in the published code for XXTEA. It may be worth taking some space here to discuss it.
Specifically, the problem is that different implementations create different results for encrypting the same plaintext and key.
Discussion:
This problem results from the addition of the length of the plaintext as the last element of the array of longs. While this solves the problem of plaintext that has a length that is not a multiple of 4, it generates a different encrypted value than is generated by the JavaScript implementation.
If you insert "$w=false;" at the start of the long2str and str2long functions, the encrypted value for the PHP implementation becomes the same as the JavaScript implementation, but the decrypted value has garbage at the end.
Here are some test case results with this change:
PHP:
text: >This is an example. !##$%^&*(){}[]:;<
Base64: PlRoaXMgaXMgYW4gZXhhbXBsZS4gIUAjJCVeJiooKXt9W106Ozw=
key: 8GmZWww5T97jb39W
encrypt: sIubYrII6jVXvMikX1oQivyOXC07bV1CoC81ZswcCV4tkg5CnrTtqQ==
decrypt: >This is an example. !##$%^&*(){}[]:;<��
Note: there are two UTF-8 question-mark characters at the end of the "decrypt" line.
JavaScript:
text: >This is an example. !##$%^&*(){}[]:;<
Base64: PlRoaXMgaXMgYW4gZXhhbXBsZS4gIUAjJCVeJiooKXt9W106Ozw=
key: 8GmZWww5T97jb39W
encrypt: sIubYrII6jVXvMikX1oQivyOXC07bV1CoC81ZswcCV4tkg5CnrTtqQ==
decrypt: >This is an example. !##$%^&*(){}[]:;<
The reason there is no garbage in the JavaScript implementation even though it does not save the length of the plaintext is given in a comment there: "note running off the end of the string generates nulls since bitwise operators treat NaN as 0". In other words, the generated string is padded with NULs that are never seen, even though JavaScript, like PHP, can include NULs in strings because it stores the length separately.
I don't have an opinion about which approach is best, but one should be chosen for all implementations.
The reason that there should be a standard for the result of encryption (regardless of whether the binary is converted to hex or to base64 for safe transit) is that one might want to use, say, PHP for encoding but JavaScript for decoding, depending on which languages are natural to use at two locations. After all, encryption is most often used to communicate between two locations, and the language used at the target location might not even be known.
Why not using the wificlientsecure library? Works great on the esp32.

PHP hashing method, same output every time

So if I do something like sha1($data) the result will be BLAHBLAH123. However if I do it again it will be BLAHAHS316. The same thing happens with md5. So my question is, what is a consistent way to hash values?
So like function($data) will return BLAHBLAHBLAH123 each time it is evaluated with the same $data parameter.
EDIT: I have a specific purpose in mind for this that isn't passwords so security isn't a concern.
EDIT: For example, md5($data) will not return BLAHBLAH every time, sometimes it'll return BLAHHHAL. I don't want that. I want it to return the same thing, BLAHBLAH everytime!
The output of a hashing operation will only change if the input has changed.
For example:
echo sha1( 'test' );
a94a8fe5ccb19ba61c4c0873d391e987982fbbd3
If you wish it to change everytime, you could append a timestamp to the input:
echo sha1( 'test'.time() )
3d68b7693768f199623f31f820b1ba29b0a58769
Hashing function are deterministic (they would be useless if this was not the case).
In computer science, a deterministic algorithm is an algorithm which, given a particular input, will always produce the same output, with the underlying machine always passing through the same sequence of states.
Consider another (eg. time) input to the domain as in sha1(microtime() . $data) if you want 'different output'. I'm not sure how useful this will be in practice.
Password hash functions use a salt (randomly generated, stored separately) as additional input so the same plain-text password will result in a different stored hash value.
Hashing method - to give the same value, but not easy to predict or decode is what i think you are looking for.
You can use use a constant string val and do a hash of that string to get the same value always, if you want to change the value you can change the constant and get a different hash value
$constStr = "hashThis";
$hashWord = md5($constStr);
// it will return the same value always, as long as the constStr is the same
Two different input with same md5 or sha1 output?
That's possible but way to hard. Take a look at here: https://crypto.stackexchange.com/questions/1434/are-there-two-known-strings-which-have-the-same-md5-hash-value
The return value will change as long as your $data variable changes, you can't get same hashing value from different strings

Laravel Crypt value changing on each reload

I'm trying to use Laravel's Crypt functionality, to simply store a value in a database and grab it later on to use. However I noticed that I was unable to decrypt this value.
My application key is a random, 32 character string. My cipher is MCRYPT_RIJNDAEL_128.
From the PHP info, MCRYPT is installed, and RIJNDAEL_128 is supported.
To test, I do the following on a GET rou:
$t = "123456789";
var_dump(Crypt::encrypt($t));
See: http://laravel.io/bin/2e9Xr#
On each page refresh, the output is a different value, which is obviously incorrect - however I have no idea why.
I'm using an EasyPHP as my dev server. However one thing I have noticed is that the application requests are significantly slow on this environment as compared to the production, Apache web server.
This makes me wonder if there is some sort of environment refresh going on each time, potentially resetting the "resources" MCRYPT is using to encrypt, and thus is different each time.
Any clues?
That is normal behavior. Every Crypt::encrypt call should produce a different output for security reasons.
Crypt is incredibly inefficient for small strings. For example, Crypt::encrypt("Hello World") outputs something like the following: eyJpdiI6Imhnb2hRazVabUNZUnVRVzFBSEExVkE9PSIsInZhbHVlIjoiTHJ4c05zcjdJZkZwWU1vRVVRMEcwZE5nTUdjQnhyM2RKWTMzSW04b1cxYz0iLCJtYWMiOiIyZjRmNDc3NGEyNGQyOGJjZGQ4MWQxYWViYzI1MjNjZTU0MmY4YTIxYTEyNWVjNDVlZDc4ZWEzNzRmN2QwM2ZiIn0=
Immediately recognizable as a base 64 string. When decoded, it becomes {"iv":"hgohQk5ZmCYRuQW1AHA1VA==","value":"LrxsNsr7IfFpYMoEUQ0G0dNgMGcBxr3dJY33Im8oW1c=","mac":"2f4f4774a24d28bcdd81d1aebc2523ce542f8a21a125ec45ed78ea374f7d03fb"}
Using Crypt, you can encrypt and decrypt large plaintexts easily without worrying about the details. But if you want to store or transmit a lot of separately encrypted entities, then you might want to consider a different approach.
So why is it like this?
(Note: the directory structures are valid for Laravel 4.2).
For one, most secure block cipher modes of operation require an IV (initialization vector), which is a bunch of random bytes with length matching the block size. Using a different IV for every ciphertext is important for thwarting cryptanalysis and replay attacks. But let's look a bit at the actual Crypt code.
Starting with the config/app.php aliases array, we see 'Crypt' => 'Illuminate\Support\Facades\Crypt'
So we check the vendor/laravel/framework/src/Support/Facades directory, and we find Crypt.php which says the module accessor name is actually "encrypter". Checking the config/app.php providers array shows 'Illuminate\Encryption\EncryptionServiceProvider'.
vendor/laravel/framework/src/Illuminate/Encryption has several files of interest: Encrypter.php and EncryptionServiceProvider.php. The service provider binds the accessor with a function that creates, configures, and returns an instance of Encrypter.
In the Encrypter class, we find the encrypt method:
public function encrypt($value)
{
$iv = mcrypt_create_iv($this->getIvSize(), $this->getRandomizer());
$value = base64_encode($this->padAndMcrypt($value, $iv));
// Once we have the encrypted value we will go ahead base64_encode the input
// vector and create the MAC for the encrypted value so we can verify its
// authenticity. Then, we'll JSON encode the data in a "payload" array.
$mac = $this->hash($iv = base64_encode($iv), $value);
return base64_encode(json_encode(compact('iv', 'value', 'mac')));
}
And there you have it. Each time you call Crypt::encrypt, it generates a new IV, encrypts the value, creates a MAC of the IV and ciphertext, and then returns a base 64 encoded JSON string of an associative array of the IV, MAC, and ciphertext. Each IV will be different, which means every ciphertext and MAC will also be different--even for the same value. Really smart if all plaintexts are large, but pretty impractical for a lot of smaller plaintexts where MACs are unnecessary overhead.
tl;dr version:
16 bytes of randomness is generated for every encrypt call, and it cascades into the ciphertext and MAC, all of which is returned in a base 64 encoded JSON associative array. Thus, every Crypt::encrypt call produces different output.
That's how mcrypt works - http://mnshankar.wordpress.com/2014/03/29/laravel-hash-make-explained/
$test = 'test';
$crypted = Crypt::encrypt($test);
echo $crypted.'<br />'; // encrypted string
echo Crypt::decrypt($crypted); // "test"

Md5 unique id good enough for hidden form tokens?

I'm using md5(uniqid()) to generate a unique hash for my token hidden input on my forms (like Log in, Sign up, Settings, etc) for my File Sharing and Hosting service and for the user session, so I can compare those two after the form is submitted.
But I'm wondering if md5(uniqid()) is good enough after I've read that md5 has lots of security flaws.
Are there better or more secure ways of generating tokens for my forms?
Output example
<input type="hidden" name="token" value="4c1dd84d3458964ee6d59c728dc70160">
This token should just be an unpredictable code. The best you can do to get such an unpredictable code with a deterministic computer, is to generate a really random number.
When you use the MD5 function with your uniqid, it does not add any randomness/unpredictability to your token, you (mis)use it as an encoder. The same goal you get with using the bin2hex() function, that's what MD5 does by default after calculating the binary hash. That said, the MD5 function is not unsafe here but has no advantage neither.
The more important point is, that the function uniqid() is not unpredictable, it is based on the current timestamp. This is the unsafe part in your code. To get an unpredictable number you can use the function mcrypt_create_iv() which reads from the random source of the operating system.
I would recommend to let PHP create the session token for you, with the session_start() function. If you really have reasons not to use a normal PHP session, then use mcrypt_create_iv() together with an encoding function like bin2hex() or base64_encode().
EDIT:
From your comments i see that this token is not used to maintain the session, instead to mitigate csrf. In this case of course the session_start function won't help (the session id should not be used as csrf token), but creating an unpredictable token is still important. This is an example of how this can be done:
/**
* Generates a random string of a given length, using the random source of
* the operating system. The string contains only characters of this
* alphabet: +/0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
* #param int $length Number of characters the string should have.
* #return string A random base64 encoded string.
*/
protected static function generateRandomBase64String($length)
{
if (!defined('MCRYPT_DEV_URANDOM')) throw new Exception('The MCRYPT_DEV_URANDOM source is required (PHP 5.3).');
// Generate random bytes, using the operating system's random source.
// Since PHP 5.3 this also uses the random source on a Windows server.
// Unlike /dev/random, the /dev/urandom does not block the server, if
// there is not enough entropy available.
$binaryLength = (int)($length * 3 / 4 + 1);
$randomBinaryString = mcrypt_create_iv($binaryLength, MCRYPT_DEV_URANDOM);
$randomBase64String = base64_encode($randomBinaryString);
return substr($randomBase64String, 0, $length);
}
There was a great answer to the first half of this on
https://security.stackexchange.com/a/19710 , the second half god a good answer from the manpage, as posted by rmcfrazier: http://www.php.net/manual/en/function.uniqid.php
quote combinatorics:
"There are devastating collision attacks on MD5. (...) In contrast, SHA1 appears to be much more secure. While there are some known attacks on SHA1, they are much less serious than the attacks on MD5. For this reason, SHA1 is a much better choice than MD5 in many settings." <- MD5
"Warning: This function does not create random nor unpredictable strings. This function must not be used for security purposes. Use a cryptographically secure random function/generator and cryptographically secure hash functions to create unpredictable secure IDs." <- uniqid
Per the man page, this should not be used for cryptographically secure tokens
http://www.php.net/manual/en/function.uniqid.php
You should use openssl_random_pseudo_bytes for your tokens
http://www.php.net/manual/en/function.openssl-random-pseudo-bytes.php
It really depends on how secure you need the token to be. Using the PHP session ID should suffice unless you need to verify the integrity of the key after submission.
Eg. PHP session id:
php > session_start();
php > echo session_id();
ritig5ecgp6ebmnq8p5imbdhl3
However, you can always generate more secure IDs using the hash() function eg:
http://us2.php.net/manual/en/function.hash.php
Don't use RipeMD
echo hash('ripemd160', 'The quick brown fox jumped over the lazy dog.');
ec457d0a974c48d5685a7efa03d137dc8bbde7e3
Unless the token needs to be cryptographically secure, the session key should suffice.

What is the password parameter to openssl_encrypt?

The PHP documentation for the openssl_encrypt functions states
string openssl_encrypt ( string $data , string $method , string
$password [, int $options = 0 [, string $iv = "" ]] )
Can somebody help me understand what the argument named $password is?
An answer could include a confirmation or rejection of the idea, that besides named $password the parameter indeed is used as the key for the encryption.
What is the password parameter to openssl_encrypt? Is it a password string (with only printable characters) or is it a key (with non-prinatable characters and ASCII-Z terminators)?
Explanation
I am stuck with the documention of PHP's openssl_encrypt. Being a nice guy and trying to do the "RTM" I cannot make much sense with the imho unsatisfying documentation.
The problem is that for me there is a difference between a password and a key when it comes to encryption. A key is directly the parameter used for encryption and hence necessarily of a specific size - "the keylength" - i.e. 128/256/512 bits depending on the cipher and keylength desired. A password on the other hand is a to my understanding a human readable string entered via the keyboard which may in difference be of any length and which is before being used to encrypt first converted into a key.
hence schematic difference:
password => key => encryption
key => encryption
Unfortunatelly in the PHP openssl_encrypt documentation I cannot find any information how to use a key. The only thing suggested is a parameter "password".
Can anybody give me a glue how a key can be used?
Surely I donot want to enter the key as the password parameter as I want a specific key to be used in encryption. I do not want this key to be simply missunderstood as a parameter and serve for another key being calculated from my "key mistaken as password".
Additionally the mistery continues looking at the documention regarding the initialization vector parameter in the same openssl_encrypt function. It simply states:
iv A non-NULL Initialization Vector.
and that iv should be a string. Given that the iv is normaly a binary data of a certain length and for instance a string terminating \0 (hex 0x00) can be occuring inside I am puzzled what format is desired.
In essence I feel very much left alone with the PHP documentation which also states
WARNING This function is currently not documented; only its argument
list is available.
Update
I did some testing, and "trying around" to help me figure out what the password parameter is.
using this code:
$pass="0123456789abcdefghijklmnob";
$iv="0123456789abcdef";
echo "using $pass results:\n";
echo openssl_encrypt("test secret string", 'aes-128-cbc', $pass,NULL,$iv);
I get this result:
using 0123456789abcdefghijklmnob results:
XjEeaLucY38Y6XEUceYMYKTebR4kOp3s727ipMl5KNc=
Then changing the length of the "password" parameter:
$pass="0123456789abcdefg"; //hijklmnob";
$iv="0123456789abcdef";
echo "using $pass results:\n";
echo openssl_encrypt("test secret string", 'aes-128-cbc', $pass,NULL,$iv);
I get still the same encrypted code:
using 0123456789abcdefg results:
XjEeaLucY38Y6XEUceYMYKTebR4kOp3s727ipMl5KNc
It seems by way of testing yet not by way of being informed by the documentation that
the password is indeed only considered up to the first 16 bytes which seem to be the 128 bit that would be the key.
It frightens me that in such a sensitive function (used for encryption) the documentation is bad and excessive input of one poorly documented parameter is not even warned about. Also I am quite convinced that password should rather be named key as it seems those 16 bytes do directly represent the key.
The $password parameter is indeed the key. The key size depends on the AES mode you're using (as you know).
As you noted in your update, for AES-128, only the first 16 characters/bytes count for the key. For AES-256, it would be the first 32 characters.
When one uses the openssl_encrypt() and openssl_decrypt() functions, one can simply pass a 32 character human-readable password for the $password/key parameter.
For example, my input for the password parameter might be $password = "This is 32 characters long......";
Most people don't like having to write up a plaintext password that is a fixed length, so they might compute a hash and truncate it to the correct length. They would use this hash as their encryption key/password.
For example, I could compute an MD5 hash of a password/phrase of any length that I would like and then use that as my AES password/key:
$plaintextPass = 'This is my password. This password is not exactly 32 bytes, but that is okay because I am hashing this.';
$password = hash('md5', $plaintextPass); /* the encryption key */
With that in place, no matter what plaintext password I use, I can have a valid 32 character/byte string as my encryption key/password. This does reduce the entropy of the encryption key/password, though, because a normal 32 character string has a larger key space than an MD5 hash output (256 possibilities per byte vs. 16 possibilities per byte); however, 16^32 is still certainly out of the range of brute force.
Off topic: In some of my personal programs, I have been working on using 32 randomly generated bytes with values between 0-255. This would make the entropy of the encryption key 256^32 which is infeasible to bruteforce. This will also be resistant to dictionary attacks because the 32-bytes are randomly generated using a cryptographically secure pseudo-random number generator (CSPRNG).
So, to sum this all up, yes, the $password parameter is indeed the key used for encryption. I agree with you that is should be written as $key in the documentation, but oh well. The password/key which you select for encryption can be humanly readable as long as it satisfies the length requirements of the hashing key. To satisfy these length requirements, one can hash a human-readable/plaintext password and use the hash of that password as the key ($password parameter), but the human-readable password should still be long and unique.
The documentation is not very clear, however this comment on the openssl_encrypt() PHP doc page really helped me when I was trying to understand the function. The comment provides an example as well as useful information. To direct quote the author of the comment:
Because the password parameter documented here is not the password.
...
It is the key!
[Comment minorly edited]

Categories