November 08, 2019

Securely sending Emails from static sites with a 3rd party API

I have been looking for a way to send emails and/or other data from a static site using a thrid party mailing API. There are a few awesome services for sending mails but none of them are 'secure'. By secure, I mean to say that whatever a person is sending to me through a mailing API is delivered as plain text. That means, anyone having access to the API server could see any mail seent to me via their API. And I don't want to setup my own back-end just to send emails, either.

The solution to this problem is prett simple! Encrypt all data before you are sending using a public key which belongs to you. And whenever it is delivered to you, decrypt with your public key.

Explanation

Okay. Here you may have a question, what the hell is a key? To answer that, a key is an unique id using which a person could encrypt a message and decrypt it on demand later on.

So, what is the benifit? Well, a lot of benifits! First, no other person than you could ever look into it(unless you share your private key). It won't let anyone modifiy the message which is being sent to you in midway.

To encrypt/cipher the plain text message, we will be using Public Key Encryption . So, before moving forward, let's learn about a few related terms related to cryptography which I will be using throughout the post.

  • Private Key: It is a cryptographic key used for decryption. By nature, it is a secrret key and should only be with you. Once you think you lost the key or it get comprimised, time to get a new one. And invalidate previous public and private key.
  • Public Key: It comes with your private key, as a pair. This key is used to do encryption so that the private key could decrypt. This key can freely be shared with anyone. A message which is encrypted with a public key must be decrypted using the private key of that pair. I have my public key here. Send encrypted message haha..
  • Fingerprint: A fingerprint is a short version(consists bytes) of a longer public key. It is used to simplify key management in certain apps. It's generated by applying a cryptographic hash function in public key. My fingerprint is 547D 3B2E 7F2B 71C7 8484 9561 CD56 CFAA 5D59 2671

Implementation

Steps to implement:

  1. We will generate a key pair for us. So that, we can encrypt/decrypt a message.
  2. Encrypt message using our own public key.
  3. Send message from our static site using a mail delivery API.
  4. Once the API delivers the message to us, decrypt using private key.
  5. That should be enough! Let's see how it goes.

Generate a Key Pair

To handle encryption, we would be using OpenPGP. OpenPGP is an encryption standard derived from PGP. GnuPG(gpg) is a software which implements OpenPGP. Generally GnuPG comes pre-installed. If you don't have it installed, download it from here.

Pretty Good Privacy (PGP) is an encryption program that provides cryptographic privacy and authentication for data communication. PGP is used for signing, encrypting, and decrypting texts, e-mails, files, directories, and whole disk partitions and to increase the security of e-mail communications. Phil Zimmermann developed PGP in 1991. - wikipedia

  • Generate a new Key from command line: Type gpg --full-gen-key in your terminal, hit enter and follow the on-screen instruction.
  • To find out fingerprint of your key, run gpg --fingerprint.
  • To export public key(you have to), run gpg --armor --export FINGERPRINT > public-key.txt. This will generate an ASCII armored public key and save it in public-key.txt.

Encrypt Data

Now that we have our key, we can go ahead and geneate encrypted text using the public key. To encryt/decrypt, we would be using a library called openPGP.js(developed by Signal). You can simply add this library to a project using CDN.

Let's start with writing some codes.

Create a form in HTML

Copy following HTML inside index.html(create file in project root)

<html>
  <body>
    <form action="">
      <input type="email" placeholder="mail@abdus.xyz" /> <br />
      <input type="name" placeholder="Abdus" /> <br />
      <textarea
        name="message"
        cols="30"
        rows="10"
        placeholder="Your awesome message"
      ></textarea
      ><br />
      <input type="submit" value="Encrypt and Send" />
    </form>
    <script
      src="https://cdnjs.cloudflare.com/ajax/libs/openpgp/4.6.2/openpgp.min.js"
      integrity="sha256-txcrHjb7Yt1zrebRnGkk5OwDkoBrvJTjvJx5b7qBtWc="
      crossorigin="anonymous"
    ></script>
    <script src="assets/js/main.js"></script>
  </body>
</html>

Also copy following JavaScript inside assets/js/main.js(create in project root)

const PUBLIC_KEY = `PUT YOUR PUBLIC KEY HERE(copy it from public-key.txt we generated earlier)`

async function encrypt(message) {
  const enc_message = await openpgp.encrypt({
    message: openpgp.message.fromText(message),
    armor: true, // export as ascii armored text
    publicKeys: (await openpgp.key.readArmored(PUBLIC_KEY)).keys,
  })

  sendMail(enc_message.data) // send the mail
}

document.querySelector("form").addEventListener("submit", e => {
  // dom manipulation
  const sender = {
    name: document.querySelector("#name").value,
    email: document.querySelector("#email").value,
    message: document.querySelector("#message").value,
  }

  // prevent page from reload on form submit
  e.preventDefault()

  // finally call our encrypt function
  encrypt(`${sender.name} <${sender.email}> \n\n\n ${sender.message}`)
})

function sendMail(encrypted_message) {
  // here, we will make a POST request to formspree
  // so that formspree could deliver our data
  fetch("https://formspree.io/YOUR_FORMSPREE_ID", {
    method: "POST",
    mode: "no-cors",
    body: JSON.stringify({
      message: encrypted_message,
    }),
  })
    .then(console.log)
    .catch(console.log)
}

Above JavaScript is pretty much self-explanatory. You shouldn't have much problem with it. If you face any issue, let me know using this form.

Also, we need a Formspree account. This is an API to deliver our form data/email. It's easy to create an account in Formspree. You can do that.

Decrypt a message

Once you recieve the message through Formspree, it's time to decrypt and see what is inside it. To do that, we would be using command line gpg client. Before decrypting, save your encrypted text in a file and name it something.pgp. Then finally, run gpg --decrypt something.pgp(assuming something.pgp is in current working directory). The output with be piped to standard output, i.e. (mostly) terminal/cmd window. To save it in a file, add this flag to the command: --output something.txt.

If you prefer a GUI application, you'll find many open-source applications for free. Just make sure it implements the OpenPGP standards.

That's it! By this time, you should have an encrypted email which is delivered securely with the help of a third-party website from your static site.

Footnote

  1. This encrypting process does not sign the document.
  2. It's always better to use your own server.
  3. Never disclose your private key. Never.