Securely Setting Up a Simple Website

This post aims at providing simple step-by-step guidance on how to set up a small, cost-efficient, yet secure website as an individual or organization. Being written by a single person using a single web site as an example, this post is highly opinionated. In order to put some of the decisions into perspective, the following paragraph outlines the project context, before we take a deeper look into the decisions themselves and the particular implementations.

Example: Primary School Website

As mentioned in an earlier post, I volunteer in the support association („Förderverein“) of my daughters‘ primary school. Currently, neither the association nor the school itself have their own website, let alone an IT department. Because I work in tech, I was approached by the school’s principal who asked for advice on how to get started building a website. They had already taken several attempts on putting up a site, but never got very far. There was a free offering available, which was provided by the state administration, but that seemed cumbersome to work with. Commercial options were considered as well, but it was hard to judge which of those were best suited for them.

Our Choice: Managed WordPress

Driven by personal interest, I have been following cyber security news for a couple of years, including blogs like troyhunt.com, scotthelme.co.uk, and podcasts like Security Now, Cyber Security Sauna, Random but Memorable, or InnoQ Security, so I wanted the site to be configured as securely as possible, but still require low maintenance effort and be cost-efficient.

While WordPress has made the cyber security headlines quite some times in the recent past, many of those incidents have been due to vulnerabilities in plugins, and lack of updates. So in order to operate a WordPress site, my priorities were clearly keeping the number of plugins to the absolute minimum, and (similar to this blog) selecting a managed offering, in which securing and updating WordPress is not primarily my own responsibility.

Since we are a (very) small non-profit organization in Germany, completely run by volunteers, responsibilities might change in the future, and therefore we voted against choosing one of the US-based heavyweights in the market, but went for a local, specialized vendor instead. They offer German customer service, including phone support, which gives us the confidence of being able to transfer responsibilities to less tech-savvy person in the future, who might also not be as comfortable communicating in English.

Regardless of this decision, the following paragraphs universally apply to any web hosting solution, given that it comes with a dedicated domain and allows for name server changes.

Optimizing the TLS Configuration

One fundamental advice given by security professionals is that a lot of security comes with secure default settings. Therefore, I tried to achieve a sensible, secure default configuration for the site. The first step was enabling HTTPS on the site itself. This could be done by activating Let’s Encrypt in the admin panel.

Since settings were limited in terms of protocol versions, HSTS headers etc, I chose to follow Troy Hunt’s advice on httpsiseasy.com and put Cloudflare in front of the site. This is basically achieved by changing the name servers of the domain over to Cloudflare’s servers, allowing them to route all of the traffic to your site though their network. Cloudflare offers many features, like caching of requests or protecting agains DDoS attacks, but we’ll focus on those features that are relevant for improving the security of your site.

After completing the TLS setup on Cloudflare as suggested over at httpsiseasy.com, our site scores an A+ on ssllabs.com.

CSP and Other Headers

One thing I wanted to do for the site was create a Content-Security Policy (CSP). Basically, a CSP is a way to define which other sources your site should be allowed to pull stuff from, with „stuff“ being images, font, style sheets, scripts, and the like. Some time ago, I came across a site selling learning toys for kids, which immediately forwarded me to a malicious site showing loads of ads. This was achieved by someone (probably a script or a botnet) using a vulnerability on their site to inject a malicious script into the page. Had they used a CSP, my browser would have just refused to load that script, since it was hosted on another domain. Also, CSP provides a means of reporting such events, but I’ll cover this in another post.

In order to find out which other relevant headers exist, I recommend checking securityheaders.com by Scott Helme, which checks your site for common, security-related headers and recommends changes, along with great explanations of what they mean and how to set them. All these features are controlled by sending HTTP headers in your site’s responses. These headers are interpreted by the browser to e.g. block certain content. Depending on your web host, you may not get full control over the HTTP headers sent by the server. This is where Cloudflare workers come into play: Workers are basically code snippets that run each time a certain request is made to your site. While there is lots of use cases for workers, we’ll focus on a very simple one: On every request, add some statically defined HTTP headers to the result. This is achieved by the following worker code (adapted from this example in Cloudflare’s documentation):

addEventListener('fetch', event => {
  event.respondWith(handleRequest(event.request))
})

async function handleRequest(request) {
  const response = await fetch(request)
  
  // Clone the response so that it's no longer immutable
  const newResponse = new Response(response.body, response)
  
  // Add securirty headers
  newResponse.headers.append("Upgrade-Insecure-Requests", "1")
  newResponse.headers.append("X-Frame-Options", "SAMEORIGIN")
  newResponse.headers.append("Referrer-Policy", "strict-origin-when-cross-origin")
  newResponse.headers.append("Permissions-Policy", 
    "accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=(), interest-cohort=()")

  return newResponse
}

Once the worker is deployed and active, we have to make sure it runs on every request, which is achieved by adding a route and assigning the worker to it. In our case, we want to run this worker on every request, so we just copy the default route and assign our new worker.

Cloudflare’s free plan includes 100.000 worker invocations per day, which should be plenty for a small website. Still, you’ll have to decide what should happen in the rare occasion that you hit the limit. Your options are failing open, meaning that your site will still be reachable, but the worker will not run, and hence security headers will not be added anymore for the rest of the day, or failing closed, meaning your site will not be reachable for the rest of the day, but you can be sure it will always have security headers when it is.

Using these modifications, our site now gets an A rating on securityheaders.com, giving us the confidence that we do our best to protect visitors from malicious activity, even in case our site gets compromised.

Part 2: Hardening WordPress

So far, all of the above settings apply to any website, wether you use WordPress or any other offering. The next post will cover additional steps specific to WordPress.

Ein Kommentar zu „Securely Setting Up a Simple Website

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden /  Ändern )

Google Foto

Du kommentierst mit Deinem Google-Konto. Abmelden /  Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden /  Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden /  Ändern )

Verbinde mit %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.