![site logo](/staticlogo.5f9619cf_ZjWG9J.jpg)
7 min read
Table of Contents:
Create an encryption and decryption app to share files securely across the internet. Instead of sending your files to the mercy of an email client, run a server that keeps information to those who need it.
This project is using vanilla PHP. No frameworks, Composer, Laravel, or any of that fancy business. Instead, this time we will show what can be done with PHP by itself. The finished code is linked below on Github
cobb208/phpencryptionapp (github.com)If you have not setup a project before in PHP don't worry! Look at the options below, this project was made in PHP8.1. No big features were used from the 8.X library so you should be safe with 7.X if you do not want to upgrade just yet.
If you are brand new or do not feel comfortable with the command line, yet I would recommend XAMPP to run a virtual machine for you. XAMPP runs a modified Linux distribution that is designed to run a webserver on any operating system.
A simple read of the documentation will show you exactly where you need to put your files. It even installs PHP for you!
Utilize Windows Subsystem for Linux to run a webserver inside of your Windows instance. This option will keep you in the familiar land of Windows but allow you to use the command line with an actual production Linux Distribution.
This option is a bit more advanced as you will need to:
I recommend this option for someone looking to learn backend web development on a Windows machine.
Utilize Homebrew on your Mac to setup the server. Homebrew works like a package manager in other Unix distributions. For my Mac this is what I choose. It bridges the gap of Mac's Unix and Linux. Follow the documentation on installation steps.
In the end, everything you are using is Linux. If you are using a Linux computer, I imagine you are comfortable with the command line and what will be discussed throughout the article. Ensure you have PHP, Apache, and MySQL installed.
I would recommend using a text editor or IDE when you work with PHP. My favorite text editor for PHP is PHPStorm . It has a lot of powerful tools and helps you work with PHP, HTML, CSS, JavaScript, and databases. However! It costs money... A free and great option is Visual Studio Code.
This is the location on most Linux distributions that Apache will setup its web server. You should have an index.html file in here to test if the website works. If you are using XAMPP spool up your server, other means ensure you start the Apache and MySQL services.
To check if your Apache service is up and running, in your web browser go to http://localhost Apache runs on the localhost alias as well as the machines IP Address. It defaults with port 80. You do not have to put the port information in the URL because web browsers know to ask port 80 first.
We will have three files, index.php, connection.php, decryption.php
Ensure you have a database setup and a user that can login into a database through localhost. My database will be named "encryptointest" and the single table structure will be:
create table files
(
id int auto_increment
primary key,
passcode varchar(255) not null,
iv varbinary(255) not null,
filepath varchar(255) not null,
sender_email varchar(200) not null,
receiver_email varchar(100) not null,
created_at datetime not null,
picked_up tinyint(1) default 0 not null,
tag varbinary(255) not null,
file_ext varchar(50) null,
constraint table_name_id_uindex
unique (id)
);
This is the setup we will use throughout the program.
This file will hold our connection string to our database. The file will just hold variables to be used in other places. This design is not always the best because you will store a password in plaintext. You can setup Apache rules to forward around this file. They will never be exposed to the internet; however, if the PHP interpreter becomes compromised on your server it can become exposed. ENV variables can solve this problem.
<?php
$sql_hostname = 'localhost';
$sql_username = 'YOUR USERNAME';
$sql_password = 'YOURPASSWORD';
$sql_database = 'encryptiontest';
The first thing we will do is import the connection file so we can use the variables within it.
<?php
require_once('./connection.php');
The HTML used in this program is basic and does not have CSS. It fits the purpose of this program and would love to be styled by you!
<form action="http://localhost/encryption/index.php" method="post" enctype="multipart/form-data">
<fieldset>
<legend>Emails</legend>
<label for="ownerEmail">Your Email:</label>
<input type="email" id="ownerEmail" name="ownerEmail" required />
<label for="receiverEmail">Their Email:</label>
<input type="email" id="receiverEmail" name="receiverEmail" required />
</fieldset>
<fieldset>
<legend>Password</legend>
<label for="password">Password:</label>
<input type="password" id="password" name="password" required />
</fieldset>
<fieldset>
<legend>File</legend>
<label for="uploadFile">File:</label>
<input type="file" id="uploadFile" name="uploadFile">
<input type="submit" value="Submit" />
</fieldset>
</form>
<h1>Retrieve</h1>
<form action="http://localhost/encryption/decryption.php" method="post">
<fieldset>
<legend>Emails</legend>
<label for="yourEmail">Your Email</label>
<input type="email" name="yourEmail" id="yourEmail" required/>
</fieldset>
<fieldset>
<legend>Passcodes</legend>
<label for="retrievePassword">Enter the pass phrase</label>
<input type="password" id="retrievePassword" name="password" required/>
<input type="submit" value="Retrieve" />
</fieldset>
</form>
We have two forms, 1. that creates the information and one that requests it.
The rest of the code will be wrapped in an IF statement to do a simple check to see if it is a POST request.
if(isset($_POST['ownerEmail'])) { ... }
$destination_path = getcwd().DIRECTORY_SEPARATOR;
$file_name_arr = explode('.', $_FILES['uploadFile']['name']);
$file_ext = end($file_name_arr);
$file_name = substr(hash('sha256', basename($_FILES['uploadFile']['name'])), 0, 15);
$target_path = $destination_path . 'uploads/' . $file_name;
$file_contents = file_get_contents($_FILES['uploadFile']['tmp_name']);
PHP comes with a lot of helper functions that let us grab directories, expand file names and grab extensions, contract string, and retrieve the file contents. Let's talk about what is happening above in a list.
The next part will use more hashing and encryption to store the information.
$passcode = hash('sha256', $_POST['password']);
$cipher = 'aes-128-gcm';
$ivlen = openssl_cipher_iv_length($cipher);
$iv = openssl_random_pseudo_bytes($ivlen);
$cipher_text = openssl_encrypt($file_contents, $cipher, $passcode, $options=0, $iv, $tag);
$fp = fopen($target_path, 'w');
fwrite($fp, $cipher_text);
fclose($fp);
chmod($target_path, 0664); // Ensure file cannot execute.
The next part is creating the SQL information and using PHP sanitization steps to clean the input.
$sender_email = filter_var($_POST['ownerEmail'], FILTER_SANITIZE_EMAIL);
$receiver_email = filter_var($_POST['receiverEmail'], FILTER_SANITIZE_EMAIL);
$date = new DateTime('now', new DateTimeZone('UTC'));
$created_at = $date->format('Y-m-j G:i:s');
$file_path = $target_path;
if(!isset($sql_hostname) or !isset($sql_username) or !isset($sql_password) or !isset($sql_database)) { die; }
$mysqli = new mysqli($sql_hostname, $sql_username, $sql_password, $sql_database);
$stmt = $mysqli->prepare(
"INSERT INTO files(passcode, iv, filepath, sender_email, receiver_email, created_at, tag, file_ext)
VALUES(?, ?, ?, ?, ?, ?, ?, ?)
");
$stmt->bind_param('ssssssss', $passcode, $iv, $file_path, $sender_email, $receiver_email, $created_at, $tag, $file_ext);
$stmt->execute();
Our program takes a file encrypts the information, stores a file, and then creates a record in a SQL database so it can be referenced later.
To test it yourself, open up the index page in your web browser, fill out the top form and submit. It should route you back to this page. If not, check your Apache's error log file for more information at why it might have failed.
Now we will create the file that takes our input, gets the file, decrypts it, and sends it back to us.
<?php
require_once('connection.php');
if(isset($_POST['yourEmail']) and isset($_POST['password'])) { ... rest of the code }
We will be wrapped in another if statement to ensure it is a post request. If someone ran a get request (normal browser request) on this file, it would simply show a blank page
$receiver_email = filter_var($_POST['yourEmail'], FILTER_SANITIZE_EMAIL);
$passphrase = hash('sha256', $_POST['password']);
if(!isset($sql_hostname) or !isset($sql_username) or !isset($sql_password) or !isset($sql_database)) { die; }
$mysqli = new mysqli($sql_hostname, $sql_username, $sql_password, $sql_database);
$sql =
"SELECT iv, filepath, tag FROM files WHERE passcode = '" . $passphrase . "' AND receiver_email = '" . $receiver_email . "' LIMIT 1";
$result = $mysqli->query($sql);
$result_filepath = '';
$result_iv = '';
$result_tag = '';
if($result->num_rows > 0)
{
while($row = $result->fetch_assoc())
{
$result_filepath = $row['filepath'];
$result_iv = $row['iv'];
$result_tag = $row['tag'];
}
}
This block of code does a lot of similar steps from the previous file so we will keep it short. The main thing to look at is the while loop. Even though the SQL is a limit 1 (only 1 item will be returned) it will still return an array. That while loop will only run once but it is required.
$cipher = 'aes-128-gcm';
$file_contents = file_get_contents($result_filepath);
$plain_text = openssl_decrypt($file_contents, $cipher, $passphrase, $options=0, $result_iv, $result_tag);
$file_prefix = explode("@", $receiver_email);
$date = new DateTime('now', new DateTimeZone('UTC'));
$date_stamp = $date->format('is');
$tmp_file = fopen('/tmp/' . $file_prefix[0] . $date_stamp . '.txt', 'w');
$true_file_name = $file_prefix[0] . $date_stamp . '.txt';
fwrite($tmp_file, $plain_text);
fclose($tmp_file);
$file_name = basename($tmp_file['name']);
header('Content-Type: multi-part/form-data');
header("Content-Disposition: attachment; filename=$true_file_name");
readfile('/tmp/' . $true_file_name);
unlink('/tmp/' . $true_file_name);
exit;
Now we get to do the opposite of encryption. All the data values stored prior are now used to help us decrypt the information. Once we have received the decrypted information. We will create the file with a special unique name for the user. Instead of just giving a hashed value as a name, the name will seem a bit more personalized showing their first part of their email (and the minutes/seconds to keep it unique).
Once the file is created, we will use headers to let the browser know what to expect. The content type is set, and the file name is given so the browser has a safe hand off of information.
The last part is to unlink, remove, the decrypted file, from our server. Only secure information is stored on the server.
Take this application and improve it. This is a concept and needs more refinement before deployment. It functions but needs error handling,
Create a simple terminal application that can let you know how much weight you need to put on the ba...
Relying on HTML to validate your forms is just one step in ensuring visitors enter their information...
NodeJS can help you automate simple tasks. Being a web developer you can achieve the same goals as o...
Python can take boring tasks away from you so you can continue to do other things. It is a scripting...
Most WordPress sites use Apache as their server. Apache has a lot of tools built into it to speed up...
@goodeveningtech
@cobb208
@cobb208_cobb
@goodeveningtech
@corycobb208