Configuring security headers

27th December 2018

Security headers are an important part of web security - not just to please Scott Helme, but to ensure that users are protected when browsing. Security headers, set by the site, specify certain rules which the browser must follow, such as which content can be loaded from where, or where images should be loaded, or whether javacscript should be on the page. If a hacker manages to get a cross-site scripting attack working on your site, a properly configured content security policy would stop the code from loading.

Sample Configuration

A good sample configuration for Apache Web Server (at /etc/apache2/sites-enabled/${site-name}.conf) might look something like the following:

Header always set Strict-Transport-Security: "max-age=3156000; includeSubDomains"
Header unset X-Powered-By
Header set X-Frame-Options SAMEORIGIN
Header set X-XSS-Protection 1;mode=block
Header set X-Content-Type-Options nosniff
Header set Content-Security-Policy "default-src 'self'"
Header set Referrer-Policy: no-referrer
Header set Feature-Policy: "vertical-scroll 'self'; animations 'self'; encrypted-media 'self'; fullscreen 'self'; image-compression 'self'; max-downscaling-image 'self';"

Meet the headers

With setting headers as our goal, how do we know what to set? The following headers are accepted, but may or may not be implemented in specific browsers:

Each of these headers should be set for every page. In my case, when I configured this site I also had to change how parts of the site were - for instance, removing inline script tags and replacing them with stylesheet calls, as otherwise my legitimate inline calls would appear as "legitimate" as a hacker's XSS-inserted code. Be aware that setting these headers may not be a simple plug-and-play task and may require fiddling.

Configuring each header

Strict-Transport-Security

Your site is secure. It uses HTTPS. You have the lock. But you should probably stop both determined users and hackers from making use of HTTP, if you can't or won't disable it. Strict-Transport-Security tells to browser that the page should be loaded over HTTPS, and any HTTP pages with this header set will be re-requested by the browser over HTTPS. Even if you have 302s or other redirects set, it's worth covering every eventuality and setting this too.

Add the following line within your VirtualHost configuration:

<VirtualHost *:443>
...
Header always set Strict-Transport-Security: "max-age=3156000; includeSubDomains"
...
</VirtualHost>

X-Frame-Options

iFrames are cool. You can load a page within a page. Some sites use this for legitimate reasons. Some sites are less scrupulous and use it to load another web page to capture input, like logins. Stop those sites by CAREFULLY and LOUDLY explaining that you don't want to be embedded by anyone other than yourself and those listed within the X-Frame-Options header.

On Apache, you can set X-Frame-Options within your Apache configuration. For me, this was at "/etc/apache2/sites-enabled/ranaldo.co.uk.conf"

Add the following line within your VirtualHost configuration:

<VirtualHost *:443>
...
Header set X-Frame-Options SAMEORIGIN
...
</VirtualHost>

X-XSS-Protection

Your user's browser has detected an XSS attack. Would you like to allow it? If no, set a secure Content-Security-Policy, and set X-XSS-Protection too, and hopefully it will be blocked.

On Apache, you can set X-XSS-Protection within your Apache configuration. For me, this was at "/etc/apache2/sites-enabled/ranaldo.co.uk.conf"

Add the following line within your VirtualHost configuration:

<VirtualHost *:443>
...
Header set X-XSS-Protection 1;mode=block
...
</VirtualHost>

X-Content-Type-Options

X-Content-Type-Options ensures that browsers do not try to guess the format (MIME type) of a resource.

On Apache, you can set Content-Security-Policy within your Apache configuration. For me, this was at "/etc/apache2/sites-enabled/ranaldo.co.uk.conf". Content Security Policy can be quite fiddly to set up and will break aspects of your site if you're not careful. I would encourage you to either spend some time like I did between Mozilla documentation, the F5 key, and the Firefox or Chrome (other browsers may vary) in their respective Developer Consoles on a test version of your site, seeing which errors are thrown with different configurations.

Add the following line within your VirtualHost configuration:

<VirtualHost *:443>
...
Header set Content-Security-Policy "default-src 'self'"
...
</VirtualHost>

Content-Security-Policy

The content-security-policy header specifies where you allow content on your site to come from. In my case, the only content on my site is self-hosted, so I can get away with the lazy option of saying all content comes from here. However, I'd still mark the specific sources over going "default-src 'none'". Start by denying everything, then one by one enable sources and locations until the site works again. For more complex sites, you'll want to exactly specify that, for instance, Google fonts come from Google and Javascript comes from maxcdn, to ensure that attackers can't load their own third-party content.

On Apache, you can set Content-Security-Policy within your Apache configuration. For me, this was at "/etc/apache2/sites-enabled/ranaldo.co.uk.conf". Content Security Policy can be quite fiddly to set up and will break aspects of your site if you're not careful. I would encourage you to either spend some time like I did between Mozilla documentation, the F5 key, and the Firefox or Chrome (other browsers may vary) in their respective Developer Consoles on a test version of your site, seeing which errors are thrown with different configurations.

Add the following line within your VirtualHost configuration:

<VirtualHost *:443>
...
Header set content-security-policy: default-src 'none'; style-src https://ranaldo.co.uk; img-src data:; connect-src 'self'; form-action 'none'; frame-ancestors 'none'; base-uri 'self'
...
</VirtualHost>

Referrer-Policy

So this is a fun policy, both 'cause it's straight-forward and because the powers that be spelt it correctly this time

The referrer policy dictates whether requests from a given page include the url as "Referer" in subsequent headers. This can be an issue when query strings are included in links to htird-party sites, as user details may be leaked. If you don't need the functionality of a referer header, set it to "no-referrer". If you need it yourself, then choose the least-specific version required to make it work.

Add the following line within your VirtualHost configuration:

<VirtualHost *:443>
...
Header set Referrer-Policy: no-referrer
...
</VirtualHost>

Feature-Policy

The feature-policy defines which browser features (ranging from expected - i.e. fullscreen, geolocation, camera, to esoteric - i.e. battery, magnetometer, publickey-credentials-get.)

This is a nice safety feature, as it means that pages one embeds (namely iframes) are unable to access features deemed inappropriate.

The feature-policy header is still being ironed out. For now, consult the specification to check its current status at w3c's site. I applied some generic whitelisted features to cover what I expected a user would require, as I don't have much (any) media on my site, but do need support for fullscreen, encrypted-media (gzipped traffic), and scrolling. Your mileage may vary on this, as sites with anything interesting will probably have a lot of fun dealing with tickets from this header. Definitely test this header before implementing.

Add the following line within your VirtualHost configuration:

<VirtualHost *:443>
...
Header set Feature-Policy: "vertical-scroll 'self'; animations 'self'; encrypted-media 'self'; fullscreen 'self'; image-compression 'self'; max-downscaling-image 'self';"
...
</VirtualHost>