Securely Exchange Files over HTTP

Need to exchange sensitive files between two machines but only have readily available operating system tools at your disposal? Here’s how to do it securely via HTTP!

The original version of this article was using the readily available openssl command. Just before publication, I stumbled across a StackExchange reply which describes in great detail why using OpenSSL for general purpose encryption is not recommended. After lengthier considerations, I decided to leave the openssl commands in but generally promote the more secure and purpose-built GnuPG encryption tools.

TL;DR: Just 8 Steps for the Impatient

If you don’t fancy a step-by-step guide or pretty screenshots, then the following basic instructions — which are correct for a contemporary MacBook and a cheap and cheerful Acer Chromebook as of 2020-07-12 — should suffice:

  1. On the Mac, cumulate the files to be transferred in directory Chromebook
  2. Zip the directory and save the output to Chromebook.zip via

     zip -r Chromebook.zip Chromebook/
  3. Encrypt Chromebook.zip and save the output to Chromebook.zip.enc via

     gpg --s2k-mode 3 --s2k-count 65011712 --s2k-digest-algo SHA512 --s2k-cipher-algo AES256 --output Chromebook.zip.enc --symmetric Chromebook.zip
  4. Move Chromebook.zip.enc to a separate directory and start a simple Python HTTP web server on port 8080 in that directory using the wrapper script web-serve-this-directory via

     web-serve-this-directory -p 8080
  5. On the Chromebook, download Chromebook.zip.enc via

     wget http://<ip-address-of-web-serve-this-directory>:8080/Chromebook.zip.enc
  6. Decrypt Chromebook.zip.enc and save the output to Chromebook.zip via

     gpg --output Chromebook.zip --decrypt Chromebook.zip.enc
  7. Unzip the files in Chromebook.zip via

     unzip Chromebook.zip
  8. Enjoy secure file transfer over a poor man’s HTTPS!

Happy? Got what you were looking for in the time you were willing to invest? Fantastic! Now, join the rest of us for how we got there in the first place along with some additional remarks on security.


The Last Leg of a Setup

It took a while to figure out a half way decent solution to installing Visual Studio Code on my cheap and cheerful Acer Chromebook. But it was well worth the effort.

With it came an abundance of familiarity, convenience, and productivity for everything from open source development to writing blog posts as “articles-as-code” in Markdown (yep, also this one).

Well, almost. While I was instantly logged into all Google services by magic of me providing the password upon initial setup, the same was not true for all other non-Google services, unfortunately. They didn’t have the slightest clue it was me trying to access them from a new device. Long story short:

I needed to transfer a substantial amount of secrets from my Mac to my Chromebook. Basically, my entire digital life.

A task which, in my books, easily qualifies as deserving some longer thoughts before eventually being approached with the necessary respect and precautions. The “slow thinking” part of Daniel Kahneman’s best seller, if you want.


Exchanging Secrets in Public

Now, as I was out of removable hard drives at this point in time (and also didn’t want any potential residue secrets lurking around on them), the transport medium of choice was WiFi.

While modern encryption on WiFi is fairly secure for everyday use (you’re using a strong password, aren’t you?!), putting my entire digital life on a couple of electromagnetic waves without any additional protection didn’t seem like the most secure thing to do. Don’t get me wrong:

I’m not paranoid. It’s just that everyone is after me!

With the transport medium sorted, it was time to find a suitable service to serve the secrets with. Unfortunately, SSH was not available and SSL certificates were also in short supply, effectively ruling out a large part of standard cryptographically secured transport mechanisms.

Installing a dedicated application seemed a bit of an overhead, given this was to be a simple one-off transfer. I wasn’t planning on freely distributing my entire digital life on a regular basis; large parts of the security concept revolved around the ephemeral nature of having to store the secrets outside of the intended target systems.

Moreover, whatever the eventual exchange mechanism, it would have to also bridge macOS and ChromeOS. Ideally, bi-directional and without having to install any additional components.

What Time Is It

When All You Have Are Standard Tools

Back to readily available standard operating system tools then that came pre-installed on both macOS and Chrome OS. Now, enabling Linux Apps (Beta) had in fact installed a Linux VM on the Chromebook with most of the standard Linux tools already on it.

At this point, what both systems did ship with was not so different from a lot of other BSD or Linux distributions. Most notably,

Both macOS and ChromeOS came with Python and OpenSSL pre-installed.

Inspired by a SuperUser reply, it turns out that

That’s sufficient to construct a poor man’s HTTPS, leveraging Python for the HTTP part while leaving OpenSSL to take care of the S(ecurity) bit.

Fantastic! Problem solved?! Unfortunately, no. As detailed in a StackExchange reply,

OpenSSL is not fit for general purpose encryption.

Not good. However, a more suitable replacement for openssl was already included in the reply as well, as it recommended the use of GNU Privacy Guard (in short: GnuPG). Best of all,

GnuPG could easily be installed on macOS via

   brew install gnupg

when leveraging Homebrew as the package manager.

While technically not entirely using readily available standard operating systems tools across all systems involved, using GnuPG did provide a more secure encryption solution. However, there were still some pitfalls to be circumvented to eventually arrive at a simple but secure poor man’s HTTPS.

List With Bullet Points for a Tutorial

A Step-By-Step Guide

In the spirit of modern systems and software architecture, the below is merely the result of carefully sellotaping together a collection of GitHub and StackOverFlow pages that provided me with valuable answers on my end-to-end journey.

On the Mac

The first part, which occurred on my Mac, involved cumulating the files to be transferred, zipping them up, and encrypting them, before eventually serving them via the Python web server wrapper script web-serve-this-directory.

Cumulating the Files to be Transferred

For convenience reasons, the HTTP transfer did only involve a single encrypted archive. Hence, the first step was to cumulate all files to be transferred in a single directory. For this, a new directory Chromebook was created via

mkdir -p Chromebook

and files secrets-1.file, secrets-2.file, and so on copied to the Chromebook directory via

cp secrets-1.file Chromebook/
cp secrets-2.file Chromebook/

Zipping the Directory

With all files to be transferred copied to directory Chromebook, the contents of the directory were zipped up to output file Chromebook.zip via

zip -r Chromebook.zip Chromebook/

Encrypting the ZIP File

Subsequently, Chromebook.zip was encrypted using gpg and the output saved to Chromebook.zip.enc, using the additional secure settings suggested in a StackExchange reply, via

gpg --s2k-mode 3 --s2k-count 65011712 --s2k-digest-algo SHA512 --s2k-cipher-algo AES256 --output Chromebook.zip.enc --symmetric Chromebook.zip

If you are aware of the security implications and are happy to accept the risks, then Chromebook.zip can also be encrypted using openssl, as suggested in a SuperUser reply, via

openssl enc -aes-256-cbc -md sha512 -salt -in Chromebook.zip -out Chromebook.zip.enc

Here, the default version LibreSSL 2.6.5 of openssl on my Mac is slightly (*cough*, *cough*) behind the official LibreSSL releases. More recent versions can again be installed via Homebrew and enabled as outlined in a StackOverflow reply.

As for the default version, providing the -md sha512 option allows to at least mitigate some of the insecurities of OpenSSL. If permitted by your version, also provide options -pbkdf2 -iter 100000 for some increased security as outlined in a StackExchange reply.

Regardless of the encryption method, make sure to provide a secure password and also remember it as it will be needed again later on. As for some general guidance on password strength

XKCD on Password Strength

Transferring the Encrypted ZIP File

Before making the encrypted ZIP file Chromebook.zip.enc available via a web server, it was moved to the dedicated directory webserver to avoid accidentally sharing other files, such as the unencrypted ZIP, file as well. This was achieved via

mkdir -p webserver
mv Chromebook.zip.enc webserver/
cd webserver/

A simple Python HTTP web server was started on port 8080 using the wrapper script web-serve-this-directory via

web-serve-this-directory -p 8080

Here, make a note of the IP address the Python web server is starting on; in my case, it was as per below screenshot.

macOS Terminal Screenshot

On the Chromebook

The second part, which occurred on my Chromebook, involved downloading the encrypted file from the Mac’s web server, decrypting, and eventually unzipping it.

Downloading the Encrypted ZIP File

On the Chromebook, the Terminal application was launched that came as part of enabling Linux Apps (Beta) and the encrypted file Chromebook.zip.enc downloaded from the Python HTTP web server running on the Mac via

wget http://<ip-address-of-web-serve-this-directory>:8080/Chromebook.zip.enc

Decrypting the ZIP File

The local file Chromebook.zip.enc was decrypted and the output saved to file Chromebook.zip using gpg via

gpg --output Chromebook.zip --decrypt Chromebook.zip.enc

In case you accepted the associated security risks by using openssl, local file Chromebook.zip.enc can be decrypted using openssl and the output saved to file Chromebook.zip via

openssl enc -d -aes-256-cbc -md md5 -in Chromebook.zip.enc -out Chromebook.zip

Here, as of 2020-07-12, the option -md md5 was required for backwards compatibility as outlined in a StackOverflow reply; note that this also indirectly points out the use of MD5 as one of the weaknesses of using OpenSSL for general purpose file encryption.

Regardless of the encryption method, supply the password provided when encrypting the file on the Mac in the first place.

Unzipping the ZIP File

The contents of Chromebook.zip were unzipped to directory Chromebook via

unzip Chromebook.zip

In case unzip is not installed on the Chromebook by default, it can be installed via

sudo apt-get install unzip

The final result on my Chromebook eventually presented itself as in below screenshot

Chromebook Terminal Screenshot

More Scalable Solutions

The process described above provides a secure file exchange mechanism built (almost) solely on readily available operating system tools across machine boundaries.

It’s great for ephemeral one-off file transfers when little else is available. However, the approach doesn’t really scale well. For repeated transfers or additional features such as audit-ability, it’s more effective to use a dedicated solution.

Options for exclusively locally provided solutions range from secure general purpose services such as SSH and HTTPS servers to specialised file services such as SFTP, NAS servers, and private clouds the likes of ownCloud or Nextcloud.

Options for internet-based file exchange services are seemingly endless and vary as providers come and go. Whether to leverage them or to keep personal data solely local is a personal choice.

Still Life USB

So, How do You Exchange Files?!

While the above Worx for Me!™ when it comes to securely exchanging files between two machines over HTTP, you may have an alternative or better way.

Think this is all rubbish, massively overrated, or heading into the absolutely wrong direction in general?! Feel free to reach out to me on LinkedIn and teach me something new!

As always, prove me wrong and I’ll buy you a pint!


Dominic Dumrauf

An Enterprise Solution Architect by profession, an avid outdoor enthusiast by heart, and a passionate barista by choice. Still hunting that elusive perfect espresso.

Read More