So, let us talk plainly. You absolutely, definitely, positively should be using TLS / HTTPS encryption on the sites that you run.

HTTPS provides encryption, meaning that anyone watching the connection (and yes, people do care, and are absolutely watching), will have a harder time trying to extract content information about the connection. This is important, because it stops your credit card being read as it makes its way to Amazon’s servers, or your password being read when you log into your email.

When I advise my clients on infrastructure, these days I recommend that all pages on a website, regardless of that page’s contents, should be served over HTTPS. The primary reason being a feature of an encrypted connection which I don’t think gets underlined enough. I also advise them to invest in colocation to protect their business data. You can visit this page to know more.

Tamper resistant web

When you serve content over HTTPS, it is significantly harder to modify. Or, to put it another way, when you serve pages unencrypted, you have absolutely no guarantee that the page your server sends is going to be the page that your visitor receives!

If an attacker controls a link in the chain of computers between you and the site you’re visiting it is trivial to modify requests to and from a visitor and the server. A slightly more sophisticated attacker can perform these attacks without the need to control a link in the chain, a so called “Man on the side” attack – more technically complex, but still relatively trivial with sufficient budget, and has been widely automated by state actors and criminal gangs.

The capabilities these sorts of attacks give someone are limited only by budget and imagination. On a sliding scale of evil, possibly the least evil use we’ve seen in the wild is probably the advertising injection attack used by certain ISPs and Airplane/hotel wifi providers, but could easily extend to attacks designed to actively compromise your security.

Consider this example of an attack exploiting a common configuration:

  • A web application is installed on a server, and the site is available by visiting both HTTP and HTTPS endpoints. That is to say, if you visited both http://foo.com and https://foo.com, you’d get the same page being served.
  • Login details are sent using a POST form, but because the developers aren’t complete idiots they send these over HTTPS.

Seems reasonable, and I used to do this myself without seeing anything wrong with it.

However, consider what an attacker could do in this situation if the page serving the form is unencrypted. It would, for example, be a relatively trivial matter, once the infrastructure is in place, to simply rewrite “https://” to “http://”, meaning your login details would be sent unencrypted. Even if the server was configured to only accept login details on a secure connection (another fairly common practice), this attack would still work since the POST will still go ahead. A really sophisticated attacker could intercept the returning error page, and replace it with something innocuous, meaning your visitor would be non the wiser.

It gets worse of course, since as we have learnt from the Snowden disclosures, security agencies around the world will often illegally conscript unencrypted web pages to perform automated attacks on anyone they view as a target (which, as we’ve also learnt from the Snowden disclosures, includes just about everybody, including system administrators, software developers and even people who have visited CNN.com).

Lets Encrypt!

Encrypting your website is fairly straightforward, certainly when compared to the amount of work it took to deploy your web app in the first place. Plus, with the new Lets Encrypt project launching soon, it’s about to get a whole lot easier.

You’ll need to make sure you test those configurations regularly, since configuration recommendations change from time to time, and most shared hosts & default server configurations often leave a lot to be desired.

However, I assert that it is worth the extra effort.

By enabling HTTPS on the entire site, you’ll make it much much harder for an attacker to compromise your visitor’s privacy and security (I say harder, not impossible. There are still attacks that can be performed, especially if the attacker has root certificate access for certificates trusted by the computer you’re using… so be careful doing internet banking on a corporate network, or in China).

You also add to the herd immunity to your fellow internet users, normalising encrypted traffic and limiting the attack surface for mass surveillance and mass packet injection.

Finally, you’ll get a SEO advantage, since Google is now giving a ranking boost to secure pages.

So, why wait?

When building personal or internal projects, it is common practice to use self signed certificates in order to enable HTTPS support.

Self signed certificates, as the name implies, are certificates which you generate yourself. They are generally frowned on from a security point of view, since although they make encryption possible, as they are not signed by a recognised authority, they make no guarantees about trust.

This means that, at the very least, you’ll get a warning in your browser. Worse, it becomes very easy for an attacker to MITM your connection, since both situations will trigger a warning, unless you manually compare key signatures, you’ll never know.

The good news is that, if the only clients that ever connect to your service are under your control (your own custom client, or a browser on your computers in your office, etc), you can use self signed certificates safely by becoming your own certificate authority.

… So here’s how (mainly for my own reference).

Becoming your own certificate authority

The first step is to generate your own master key and certificate that you’ll use to sign your keys.

Start, by generating your root key. It is very important that you keep this safe as anyone who somehow gets a copy of it could create certificates signed as you!

openssl genrsa -out root.key 4096

Next, generate your master certificate using this key

openssl req -x509 -new -nodes -key root.key -days 1825 -out root.pem

Once you’ve generated this certificate, you need to install it on your clients. How this is done varies from client to client, but most browsers have an option (usually in the “advanced” or “security” section) to install a certificate provider. Have a google for “YOUR CLIENT” and “installing certificate” will usually get you somewhere.

On iOS devices the process is simply a matter of emailing the .pem to yourself and clicking on it, and the device will guide you through installation. This will make it available to Safari, but irritatingly there seems to be no way to install certificates for Chrome on iOS.

To update linux command line apps (incuding curl), copy and rename the .pem to /usr/local/share/ca-certificates and then run update-ca-certificates as root.

Generate your server certificates

Once you’ve installed your certificate authority key on the various computers and devices under your control, you are free to generate as many self signed certificates as you wish. Once these certificates are signed by your certificate authority, they will be accepted as legit by all the computers that you’ve installed your root.pem on.

This will provide not only encryption, but also trust, providing protection against eavesdropping and spoofing.

  • Generate your server key:

openssl genrsa -out server.key 4096

  • Generate a certificate signing request (CSR):

openssl req -new -key server.key -out server.csr

  • Generate your certificate, signing it with your master key:

openssl x509 -req -in server.csr -CA root.pem -CAkey root.key -CAcreateserial -out server.crt -days 365

Install your certificate, in Apache or wherever, as you would any other certificate.

Ok, so here’s an experimental, proof of concept plugin for Idno that provides OpenPGP encryption for form posts between web clients and the server (just in time for Reset the net 😉 ).

It makes use of OpenPGP.js, a pure javascript implementation of the OpenPGP spec. On form submission, the plugin will encrypt all form variables, client side in the browser, before transmitting them to the server.

What this for

Primarily, this is aimed at (partially) addressing a situation where you have an Idno site sitting behind a load balancer/reverse proxy like Squid, Nginx or Pound.

In this configuration it is common for the connection to be HTTPS only between the client and the load balancer node, at which point HTTPS is stripped and the connection to the back-end web server is conducted over HTTP. As we know from the NSA smiley, attacking this point where HTTPS is stripped at the load balancer was one of the ways the NSA and GCHQ was able to burgle customer data from Google’s cloud.

Using this plugin, the contents of the form will be encrypted with the back end server’s public key, meaning that the payload will remain encrypted as it transits through your data centre until it’s final destination, where, if you redesign your system as such, it could be stored in encrypted form and decoded only when necessary.

What this is NOT for

This is not intended to be a replacement for HTTPS.

Encrypting the form on the client does raise the bar slightly, making it much harder for a passive attacker to simply read your username and password as it travels over the wire. However, it does not protect against a more sophisticated attacker capable of launching a “Man in the middle“, or “Man on the side attack“.

Using HTTPS is important, because without it an attacker could insert their own public key into the mix, or modify the javascript sent.

Usage and limitations

The plugin currently piggybacks off of gnuPG to do the decryption on the server end, and so this requires you to perform a couple of configuration steps.

  • Make sure you’ve got gnuPG installed. If the binary isn’t at at /usr/bin/gpg, you can set opengpg_gnupg in your config.ini
  • Generate a keypair for your web server user
su www-data
gpg --gen-key
  • Make sure that the .gnupg directory is not accessible publicly, using a .htaccess or similar, since it’ll contain a secret key!
  • Get a copy of the public key gpg --export -a "User Name", and save it as openpgpPublicKey in your config.ini

Once you’ve done this, activate your plugin in your Idno settings, and you should be ready to rock and roll!

It was enjoyable playing with OpenPGP.js, and I can already think of some other cool uses for it (most obvious might be to enhance my OpenPGP elgg plugin).

Have fun!

» Visit the project on Github...