Encrypted Contact Form in Vue

By: David Boland

Hero with logos for vue, node, and keybase over screenshot of contact form

encryption

I have always been a big believer in digital privacy. I try to take extra steps to secure my personal data whenever necessary. One of the biggest difficulties in doing that is when it comes to communication. Communication requires the individuals at both ends maintain security discipline.

Being in web development, I realized you can control how a user sends you data through a contact us form. I implemented this form to allow users to encrypt the message they write before they submit it. It's built using Vuejs for the form, and Netlify functions for handling messages on the server. You can view the code in my Github Repo.

The Encryption

The way this contact us form works, is by encrypting the users message using PGP. The benefit of how this form implements its encryption is that it's all done in the browser. Because the plain text message is not sent to the server, it can't be read if its captured in transit. So all the data is encrypted end-to-end.

PGP

If you have ever gone to my about page, you may have seen that I share a link to my PGP public key. If you are not familiar with PGP (Pretty Good Privacy) encryption, it's a way of signing, encrypting, and decrypting messages. PGP encryption is a big topic that is worthy of its own post. I won't go into that kind of detail here, but I encourage you to read up on it more.

For the purposes of this post, the important part to know is that each user has their own public and private key. Public keys are shared. They allow other user's to encrypt data that is just for you. Your private key, which you never share, decrypts those messages. If you want to learn more about public key encryption, I recommend checking out this tutorial from the EFF.

This form uses a public key to encrypt the data the user adds to the form. Whoever owns the inbox the form submits its messages to, would own the private key.

Keybase

The main limitations of PGP encryption, and most secure communication, is adoption. Many people feel it's not worth the effort to secure their messages. Keybase provides a host of services from chat to file storage. Their goal is to make encryption more user friendly.

There could be a whole post dedicated to Keybase as well. I wanted to call them out because I encourage people to check it out if they are interested in encryption. But I also wanted to note that I used their Node plugin for implementing the encryption in the browser.

animation of contact form where user enters in information, hits button to encrypt, then hits a submit button and sees a success message.

The Dev

As I mentioned, all the code is available in my Github repository. There are two main components. The site, which consists of the form. And a Netlify Function for handling posting of the form and sending the email.

The Form

The form is a basic node based site with a VueJs front end. It has one component in ContactForm.vue. You will find several functions you would expect to find in a contact form, such as submit and validation.

Some functions that are worth noting are the ones related to encryption. One of which is buildKeyManager.

// build keymanager from public key in config
buildKeyManager: function() {
    var promise = new Promise(function(resolve, reject) {
    kbpgp.KeyManager.import_from_armored_pgp(
        {
        armored: process.env.VUE_APP_PGP_PUBLIC_KEY
        },
        function(err, manager) {
        if (!err) {
            resolve(manager);
        } else {
            reject(err);
            console.log("managerError", err);
        }
        }
    );
    });
    return promise;
},

This calls the kbpgp.js function for building a key manager based on the public key that's provided. I added this key as part of the environment variables. Once the key manager is built, it can be used to encrypt.

I wanted to note that I added a public key to the .env file. I am hosting the site via Netlify. However, their functionality for adding environment variables through their we interface doesn't seem to hold the public key correctly. I assume it's because it doesn't handle multi-line properties well. If anyone knows how to implement this please let me know.

The other function, encryptMessage, encrypts the data and refills the form with the encrypted content.

encryptMessage: function(message) {
      var _this = this;
      this.buildKeyManager().then(
        function(manager) {
          var params = {
            msg: message,
            encrypt_for: manager
          };

          kbpgp.box(params, function(err, result_string, result_buffer) {
            console.log(err, result_string, result_buffer);
            _this.message.text = result_string;
            _this.waitingForAction = false;
          });
        },
        function(error) {
          console.log(error);
        }
      );
    }

The Server Function

The server function is fairly simple, so I won't go into much detail. Only to note that I use nodemailer to handle sending the emails. I store the credentials in environment variables. I then send back a success or failure message that gets displayed to the end user.

One thing to note is that I append the current date to the email subject. This is to prevent all the emails getting chained together in your inbox.

Reading Your Messages

There are many tools for encrypting and decrypting PGP messages. If you plan on looking into Keybase, they have both browser and command line options for doing so. I advise you take care of your private key. Don't use any tools you don't trust. Also, if you lose your private key or need to regenerate it, all messages encrypted with your current public key will be lost.

Future Improvements

For some next steps in this implementation, I want to incorporate verification. This form is still subject to man in the middle attacks. A cool feature would be the ability to verify the public key used in the form before submitting. This would ensure that the site visitor is in fact encrypting for the site owner.

Final Thoughts

I hope this post gets you interested in the idea of encryption, or digital privacy in general. As I mentioned previously, secure communication relies on discipline on both sides. As developers, we need to work help make digital security possible for everyone.

If you have any questions around the implementation, as always feel free to reach out. I will be adding this form to my project page with more technical information. If you end up implementing this form, or something similar, let me know. I would love to see others make this their own.