I am creating a web app (HTML, CSS, JavaScript, PHP & MySQL) where the users register, and only logged users can create and save personal Notes (encrypted) in a MySQL Server database I have for this.
I don't like external libraries, etc. I like pure PHP. With the 2 functions I have created, the idea is that not even I as the developer/provider can decrypt the Notes the users have. I like transparency, and these 2 functions will be published on the website so everyone can see how the encryption/decryption is performed. I think this will generate Trust in my users, knowing that not even I as the provider can decrypt their notes, only they can do it.
Important
I have a file named constants.php
that has the following content:
<?php
define('ITERATIONS', '100');
define('HASH_LENGHT', '128');
define('PEPPER_KEY', '0187H{UM7*JkbYJFhFG&');
define('PEPPER_IV', 'MlI@J86yJK^45bvCXaWeN');
define('PEPPER_RANDOM_SALT',
[
'LO86ti6uJ^4v6$^#4c35vhng227',
'KUh5rBKNm547bVC6V%b7237c4ee',
'FCVrvyjMNBvCE4Y5YuyiuK9PL9m',
'KUGbj4hIH6H5rFGbvr5UHTNHHCM',
'MNjH65tCvH2rwEHbhnj87543d34',
'6yuhKLmuyngb43x%$$x#cn$$%()',
'$^v546&##75B*^%^(*$#5#31254',
'nnui66544G34IYIv5$rR5Y34545'
]);
?>
Step 1: Registration
register.html
has a form where the user enters his personal data and the data is sent to the page register.php
that has this content:
require("constants.php");
/* Clean Form data */
$email = htmlentities(strtolower($_POST["email"]), ENT_QUOTES, "UTF-8");
$password = htmlentities($_POST["password"], ENT_QUOTES, "UTF-8");
/* Get a Pepper Random Salt */
$number = array_rand(PEPPER_RANDOM_SALT, 1);
$random_salt = PEPPER_RANDOM_SALT[$number];
/* Combine the password with the Random Salt */
$hash = hash_pbkdf2("sha256", $password, $random_salt, ITERATIONS, HASH_LENGHT);
...
...
Here I validate if the user already exists in the MySQL table, if not, I create the user and
the password that will be saved in the MySQL database is going to be $hash
...
...
What I am doing here is, I get the password the user entered in the form and I get (1) random Pepper I use as Salt. I know the PHP function password_hash()
exists for this but, I like to create the hash by my own way.
Step 2: Login
The page login.html
has a Form where the user enters his email & password. The data is sent to login.php
which has this content:
require("constants.php");
/* Clean Form data */
$email = htmlentities(strtolower($_POST["email"]), ENT_QUOTES, "UTF-8");
$password = htmlentities($_POST["password"], ENT_QUOTES, "UTF-8");
/* Combine the Password with each one of the Random Salts trying to find a match */
foreach(PEPPER_RANDOM_SALT as $random_salt){
$hash = hash_pbkdf2("sha256", $password, $random_salt, ITERATIONS, HASH_LENGHT);
$stmt = $db->prepare("SELECT * FROM `login` WHERE `email` = ? AND `password` = ?");
$stmt->execute(array($email, $hash));
if($stmt->rowCount() > 0){
@session_start();
$_SESSION["email"] = $email;
$_SESSION["passphrase"] = $hash;
exit("VALID");
}
}
/* Destroy Session */
@session_start();
@session_unset();
@session_destroy();
exit("INVALID");
What I am doing here is, I get the email and password the user sent, then I go one-by-one of the Random Peppers Salt I use as Salt and I generate a hash. Then I try to find if that hash exists in the MySQL database. If there is a match, I start a PHP Session and I see the variables $_SESSION["email"]
and $_SESSION["passphrase"]
.
Step 3: Encrypt
Let's assume the user is already logged in. The user is on the page new_note.html
where there is a textarea where the user writes his note, he sent the data to the page encrypt.php
that has this content:
Note: The variable $data
is the data coming from the textarea.
require("constants.php");
/* Get the Passphrase from the Session */
@session_start();
@$passphrase = $_SESSION["passphrase"];
/* Validate if Session is active */
if($passphrase == NULL) { exit("Passphrase is required to Encrypt"); }
/* Get one Random Salt */
$number = array_rand(PEPPER_RANDOM_SALT, 1);
$random_salt = PEPPER_RANDOM_SALT[$number];
/* Combine the Passphrase + Random Salt + Pepper Key */
$key = hash_pbkdf2("sha256", $passphrase.PEPPER_KEY, $random_salt, ITERATIONS, HASH_LENGHT);
/* Combine the Passphrase + Random Salt + Pepper IV */
$iv = hash_pbkdf2("sha256", $passphrase.PEPPER_IV, $random_salt, ITERATIONS, 16);
/* Encrypt */
$output = openssl_encrypt($data, "AES-256-CBC", $key, 0, $iv);
echo $output;
To generate $key, I get the variable $_SESSION["passphrase"]
generated when the user logged in and I combine it with the Pepper Key (found in constants.php) and I use (1) Random Pepper Salt as Salt. Then, I generate the hash.
To generate $iv, I get the variable $_SESSION["passphrase"]
generated when the user logged in and I combine it with the Pepper IV (found in constants.php) and I use (1) Random Pepper Salt as Salt. Then, I generate the hash.
I then, proceed to Encrypt the data in the variable $data. This encrypted data is going to be saved in the MySQL database.
To give you an example, these are the real output of the string This is a demo:
- sO26BJpswoHdiz/12FmkUg==
- FL2hufBtea6TybOujbtg0w==
- 6okuXFat5qmkuzlLkmza7w==
So, it generates different outputs for the same string which is AMAZING!
Step 4: Decrypt
When the user wants read one of his Notes, the data has to be decrypted. I use the following script:
Note: The variable $data
is the data coming from the MySQL database.
require("constants.php");
/* Get the Passphrase from the Session */
@session_start();
@$passphrase = $_SESSION["passphrase"];
/* Validate if Session is active */
if($passphrase == NULL) { exit("Passphrase is required to Decrypt"); }
/* Combine the Passphrase with each one of the Random Salts trying to find a match */
foreach(PEPPER_RANDOM_SALT as $random_salt){
/* Combine Passphrase + Random Salt + Pepper Key */
$key = hash_pbkdf2("sha256", $passphrase.PEPPER_KEY, $random_salt, ITERATIONS, HASH_LENGHT);
/* Combine Passphrase + Random Salt + Pepper IV */
$iv = hash_pbkdf2("sha256", $passphrase.PEPPER_IV, $random_salt, ITERATIONS, 16);
/* Decrypt */
$output = openssl_decrypt($data, "AES-256-CBC", $key, 0, $iv);
if(!$output == NULL){
echo $output;
}
}
To generate $key, I get the variable $_SESSION["passphrase"]
generated when the user logged in and I combine it with the Pepper Key (found in constants.php) and I use (1) Random Pepper Salt as Salt. Then, I generate the hash.
To generate $iv, I get the variable $_SESSION["passphrase"]
generated when the user logged in and I combine it with the Pepper IV (found in constants.php) and I use (1) Random Pepper Salt as Salt. Then, I generate the hash.
I then, proceed to create a hash using each one of the Pepper Random Salt as Salt expecting for an output. If there is an output, that means, the data could be decrypted using the Pepper Salt X and then, I show the decrypted data. If the output is empty, means the data could not be decrypted.
Overview
I am not creating an Encryption/Decryption model or something like that. I am just generating the hashes for $key
and $iv
in my own way, the data at the end is encrypted/decrypted using the built-in PHP functions openssl_encrypt
and openssl_decrypt
. I think the way I am doing it is fine but, feedbacks and any suggestions are very welcome to make this really secure. I have discovered the Peppers 2 days ago and I am using them. The idea of the Peppers is that, if my MySQL Database gets hacked or something, the hacker does not have a way to encrypt/decrypt data because there Peppers are not saved in the MySQL Database. The peppers are inside the file constants.php
so, the hacker has no way to guest the constants PEPPER_KEY
, PEPPER_IV
and each one of the PEPPER_RANDOM_SALT
. The Peppers I posted in this page, are dummy. In my website I have different Pepper values.
The source code of the file constants.php
is NEVER going to be released. The only source code I am going to release is the content of Step 3 and Step 4. So whoever has technical background in PHP, can see how the encryption/decryption is performed and to realize that I never save clear-text data. I only save the encrypted data in the database. I am looking for full transparency where only the user knows how to decrypt data for his Notes.
Note: I am not a PHP developer, I just learned PHP by reading example codes posted everywhere on internet pages. I am just a simple Call Center agent but, I was able to accomplish these codes and I am very happy for it. Later on, I am planning to apply as a PHP developer for a company as Remote employee to have more time because this Call Center job is taking me nowhere.