I am building a custom profile for a component I am writing. I added a password field that I encrypted with php code. That all worked. I will show my steps below. The weirdness start when in the component I used JUserHelper::getProfile to extract the value, expecting to get the encrypted value that I should decrypt. I was greeted with the already decrypted value.
So my Question is where do getProfile get the key to decrypt the value by itself. (At this point I am questioning the php encryption function if it is so easy to decrypt the field.)
System Information:
Ubuntu 16.04 server
PHP 7.0.14
Joomla 3.7.3
Steps I took
Firstly I followed the steps of https://docs.joomla.org/Creating_a_profile_plugin to create my custom profile plugin.
I added an password field to the custom profile plugin.
<field name="password" type="password" description="PLG_USER_PROFILE_FIELD_PASSWORD_DESC_SITE" filter="string" label="PLG_USER_PROFILE_FIELD_PASSWORD_LABEL" size="250" />
In the "onUserAfterSave" I added the following code that I got from the PHP help page "https://secure.php.net/openssl_encrypt" just before the component reinserts the profile values. (Could not find a way to encrypt the value in the "onUserBeforeSave", but luckily for some or other reason in the "onUserAfterSave" the function drops everything and reinsert the values which give me a change to encrypt the value.)
//$key previously generated safely, ie: openssl_random_pseudo_bytes $plaintext = $data['customprofile']['password']; $ivlen = openssl_cipher_iv_length($cipher="AES-128-CBC"); $iv = openssl_random_pseudo_bytes($ivlen); $key = 'My secret key generated from content'; $ciphertext_raw = openssl_encrypt($plaintext, $cipher, $key, $options=OPENSSL_RAW_DATA, $iv); $hmac = hash_hmac('sha256', $ciphertext_raw, $key, $as_binary=true); $data['customprofile']['password'] = base64_encode( $iv.$hmac.$ciphertext_raw );
Then I needed to decrypt the answer before the profile is displayed, so the unencrypted value is showing in the password field so the user can resave or change it. So I added in the "onContentPrepareData" the decrypt code.
if ($k == 'password') { //decrypt later.... $c = base64_decode(json_decode($v[1], true)); $ivlen = openssl_cipher_iv_length($cipher="AES-128-CBC"); $iv = substr($c, 0, $ivlen); $hmac = substr($c, $ivlen, $sha2len=32); $ciphertext_raw = substr($c, $ivlen+$sha2len); $key = 'My secret key generated from content'; $original_plaintext = openssl_decrypt($ciphertext_raw, $cipher, $key, $options=OPENSSL_RAW_DATA, $iv); $calcmac = hash_hmac('sha256', $ciphertext_raw, $key, $as_binary=true); if (hash_equals($hmac, $calcmac))//PHP 5.6+ timing attack safe comparison { $data->customprofile[$k] = $original_plaintext; } } else { $data->customprofile[$k] = json_decode($v[1], true); }
Cool, everything works I got an encrypted value in the database. I even tested if I supplied the wrong key the value is not decrypted above.
Next I go to the component where I needed to extract the password. And what do I find JUserHelper::getProfile is so nice it decrypts the value for me. No decryption code needed. (And this is where I throw my hands in the air and ask what is happening here, then any hacker would be able to decrypt the value no key needed.). See below the code for get profile, and that code is enough for me to use the password.
$profileuserId = $joomlauser->id; $userProfile = JUserHelper::getProfile( $profileuserId ); $passwordvalue = $userProfile->customprofile['password'];
Can anyone explain why it's so easy to decrypt the value. I wanted to save the password the correct way not in plain text, and now it looks like I am just obscuring the value.