Encryption is often a topic for security, but it's important for privacy too. The goal of encryption is to prevent others from reading the encrypted information… but preventing others from reading your information is one way to keep it private. A user is often limited in how much they can do that themselves, but with your assistance as provider of a service they're using, encryption can help to keep their data theirs.
There are three relevant ways to apply encryption to aid user privacy: encryption in transit, encryption at rest, and encryption end-to-end:
- Encryption in transit is keeping data encrypted between the user and your site: that is, HTTPS. You probably have HTTPS set up for your sites already, but are you sure all the data in transit to your sites is encrypted? This is what redirection and HSTS are for, and they are described below and should be part of your HTTPS setup.
- Encryption at rest is the encryption of data stored on your servers. This protects against data breaches and is an important part of your security stance.
- End-to-end encryption is the encryption of data on the client before it ever reaches your server. This protects user data even from you: you can store your users' data, but you can't read it. This is difficult to implement, and isn't suitable for all types of application, but it's a strong aid to user privacy, because nobody can see their data other than themselves.
To serve your website over HTTPS you will need an SSL certificate. These can be created for free via Let's Encrypt, or can often be provided by your hosting service if you are using one. It is also possible to use a third-party service which "proxies" your web service and can provide HTTPS, as well as caching and CDN services. There are numerous examples of such services, such as Cloudflare and Fastly—exactly which to use depends on your current infrastructure. In the past, HTTPS could be burdensome or expensive to implement, which is why it tended to be used only on payment pages or particularly secure origins; but freely available certificates, standards improvements, and greater proliferation of browsers have removed all those obstacles.
- Enable HTTPS on your servers for everything (whichever method you choose).
- Consider using a proxy in front of your servers, such as Cloudflare (httpsiseasy.com/ explains the process).
- Let's Encrypt will walk you through the process of creating your own Let's Encrypt SSL certificate.
- Or use OpenSSL directly to create your own certificate and have it signed by your choice of certificate authority (CA) (Enable HTTPS explains how to do this in detail).
Which approach you choose depends on business tradeoffs. Having a third-party manage the SSL connection for you is the easiest to set up, and does come with other benefits such as load balancing, caching, and analytics. But it also comes with an obvious ceding of some control to that third-party, and an unavoidable dependency on their services (and possible payment, depending on the services you use and your traffic levels).
Generating certificates and having them signed by a CA is how the SSL process used to be conducted, but using Let's Encrypt can be easier if it's supported by your provider or if your server team is technically adept enough for it, and it's free. It's also common for your provider to offer SSL as a service if you're using something at a higher level than cloud hosting, so it's worth checking.
How browsers present an HTTP (not secure) page
Redirect to HTTPS
If your site is available on both http: and https: URLs, you should redirect all http URL accesses to https. This is for the reasons above, and it also ensures that your site won't show up on whynohttps.com if it becomes popular. How to do this depends a great deal on your infrastructure. If you're hosted on AWS you can use a Classic or Application load balancer. Google Cloud is similar. In Azure you can create a Front Door; in Node with Express check for request.secure; in Nginx catch all port 80 and return 301; and in Apache, use a RewriteRule. If you're using a hosting service, it's quite likely that they'll automatically handle redirecting to HTTPS URLs for you: Netlify, Firebase, and GitHub Pages do, among many others.
HSTS is short for "HTTP Strict-Transport-Security", and is a way of locking a browser into using HTTPS for your service forevermore. Once you're happy with your migration to HTTPS, or if you've already done that, then you can add a Strict-Transport-Security HTTP response header to your outgoing responses. A browser which has accessed your site before will record having seen this header, and from then on will automatically access this site as HTTPS even if you request HTTP. This avoids going through your redirect as above: it's as if the browser silently "upgrades" all requests to your service to use HTTPS.
Similarly, you can serve an Upgrade-Insecure-Requests
header along with your pages. This does something different from but related to
Strict-Transport-Security. If you
Upgrade-Insecure-Requests: 1, then requests from this page to other resources (images, scripts) will be requested
as https even if the link is http. However, the browser will not re-request the page itself as https, and the browser will
not remember for next time. In practice, Upgrade-Insecure-Requests is useful if you're converting an existing site with
lots of links to HTTPS and converting the link URLs in content is hard, but it's better to change the content where possible.
HSTS is primarily a security feature: it "locks" your site to HTTPS for users who have been there before. However, as above, HTTPS is good for privacy, and HSTS is good for HTTPS. Similarly, Upgrade-Insecure-Requests isn't really needed if you're updating all your content, but it is a useful “belt-and-braces” approach to add defence in depth in ensuring that your site will always be HTTPS.
Add the HSTS header to your outgoing responses:
Strict-Transport-Security: max-age=300; includeSubDomains
The max-age parameter dictates how long, in seconds, the browser should remember and enforce the HTTPS upgrade. (Here we set it to 300 seconds, i.e., five minutes.) Eventually you would want this to be 6,3072,000, which is two years, and is the figure that hstspreload.org recommends, but it is quite difficult to recover if there are issues. So it is recommended that you set this with a low number at first (300), test to confirm nothing has broken, and then increase the number in stages.
Upgrade-Insecure-Requests headers to your outgoing responses:
A good way of keeping user data private is to not show it to anyone other than the user, including you. This helps a lot with your trust stance: if you don't have your user's data then it is clear that you can't do anything with it that they would not want. One way to do this is to not let user data leave their device at all, by storing everything client-side. This approach works, but there are limitations to a pure client-side application: browser data storage can be limited in size, and in some browsers may be cleared with little or no warning. It's also difficult or impossible to access your data across two devices, such as a laptop and a mobile phone. For this reason, it can be useful to send data to the server as normal, but encrypt it with a key known only to the user, so that the server cannot access it (because it can't decrypt it) but can store it.
How does it work?
An example: Excalidraw
Excalidraw does this and explains how in a blog post. It is a vector
drawing app that stores drawings on the server, which are encrypted with a randomly chosen key. Part of the reason that
Excalidraw can implement this end-to-end encryption with relatively little code is that cryptographic libraries are now built
into the browser with window.crypto, which is a set
the algorithms comes with many edge cases. Having the browser do the heavy lifting here makes encryption more accessible to
web developers and therefore makes it easier to implement privacy via encrypted data. As Excalidraw describes in their writeup,
the encryption key remains on the client-side, because it's part of the URL fragment: when a browser visits a URL
https://example.com/path?param=1#fraghere, the path of the URL (
/path) and the parameters (
param=1) are passed to the server
example.com), but the fragment (
fraghere) is not, and so the server never sees it. This means that even if the encrypted
data goes through the server, the encryption key does not and thus privacy is preserved because the data is end-to-end encrypted.
It's also important to remember that one of the goals of end-to-end encryption is to stop you, the site owner, from being able to read the data. This is good for privacy, but it also means that if there are problems, you can't help. In essence, a service using end-to-end encryption puts the user in charge of the encryption keys. (This may not be obvious or overt, but someone has to have the key, and if data is kept private from you, then that's not you.) If those keys are lost, then there will be nothing you can do to help, and probably any data encrypted with those keys may also be lost. There is a fine balancing act here between privacy and usability: keep data private from everybody using encryption, but also avoid forcing users into having to be cryptology experts who manage their own keys in a secure manner.
Encryption at rest
As well as encrypting your users' data in transit, it's also important to consider encrypting data that you have stored on the server. This helps to protect against data breaches, because anyone who obtains unauthorised access to your stored data will have encrypted data, which they will hopefully not have the keys to decrypt. There are two different and complementary approaches to encrypting data at rest: encryption that you add, and encryption that your cloud storage provider adds (if you're using a cloud storage provider). The storage provider encryption doesn't provide much protection against data breaches via your software (because storage provider encryption is usually transparent to you as a user of their service), but it does help against breaches that happen at the provider's infrastructure. It's often simple to turn on and so is worth considering. This field changes rapidly and your security team (or security-savvy engineers on your team) are the best to advise on it, but all cloud storage providers offer encryption at rest for block storage Amazon S3 by setting, Azure Storage, and Google Cloud Storage by default, and for database data storage AWS RDS, Azure SQL, Google Cloud SQL among others. Check this out with your cloud storage provider, if you're using one. Handling encryption of data at rest yourself to help protect user data from data breaches is more difficult, because the logistics of securely managing encryption keys and making them available to code without also making them available to attackers is challenging. This isn't the best place to advise on security issues at that level; talk with your security-savvy engineers or dedicated team about this, or external security agencies.