Modern web browsers have a number of settings to help protect your site from attack. You turn them on by use of various header lines in your HTTP responses. Now when I first read about them I thought they were not useful; a basic rule for client-server computing is “don’t trust the client”. An attacker can bypass any rules you try to enforce client side.
But then I read what they do and realised that they are primary to help protect the client and, as a consequence, protect your site from being hijacked. For example, if you can define exactly where javascript can be run from then an attacker will find it harder to inject rogue scripts and steal credentials. We can help stop XSS attacks.
What these headers do is stop the legitimate normal user of your site being used as an attack vector. This makes them an import part of the controls surface of your web presence. You still need to make sure your app isn’t vulnerable to SQL injection, buffer overflow and everything else; these headers (mostly) protect against a third-party attack.
What header values are available?
Fortunately, Scott Helme has done an excellent series of posts that describe the headers. I won’t repeat all of his work, but I will summarise and link to the relevant pages. They’re well worth reading.
Server
This one is simple to understand; if you report the version of the software you’re using then attackers can easily determine what vulnerabilities may be present. Knowing you run Apache 1.2.3 gives a lot more information to attackers then if you reported “FooBarBaz”.
Strict-Transport-Security
This tells the client to always use https for your site. It can cache this value so even if the user types in a non-https address the browser will automatically switch to the TLS version.
X-Content-Type-Options
This one really shouldn’t be needed, but is due to Microsoft being too clever. When you send a file via a https connection you include a content-type header. This should be used to help the browser decide what to do with it. But Internet Explorer ignores this and looks at the data itself; if it looks like a word document then it’ll open it in word, even if the content-type says differently. This header allows you to turn off that feature.
X-XSS-Protection
Modern web browsers have a level of built in protection to stop cross-site scripting. This header turns it on.
X-Frame-Options
When I first came across this it annoyed me. It stopped me from using my own ‘bookmarks’ frame. For the past 20 years my home page was a framed site; on the left were my normal bookmarks and on the right was where the content loaded. Very very old school. X-Frame-Options stopped this working on sites like Dilbert or GPF. Grrr. But it has a good rationale; it stops a site from being embedded in a frame and so can protect against click-jacking. Grump.
Public-Key-Pins
This allows you to restrict what SSL certificates can claim to be you. For example, if you know you will always get a cert from Lets Encrypt then you can flag this. In this way you can help protect against a rogue CA also releasing a cert under your name. We’ve seen this happen before.
Content-Security-Policy
This is a biggie and important protection and is easily misconfigured and can break your site. And, indeed, first time I hit Scott’s site it wasn’t working properly because site changes weren’t being properly reflected in the policy.
It can also stop you from doing stuff; e.g. I can’t embed YouTube videos on this site because the policy I wrote locks it down very tightly.
You will spend most of your time on this entry and will have to do the most testing.
My configuration
Because I use Apache I can’t turn off the whole Server header, but I can remove the version number. But I can do pretty well for the rest:
ServerSignature Off
Header set Strict-Transport-Security "max-age=31536000; includeSubdomains"
Header set X-Content-Type-Options "nosniff"
Header set X-XSS-Protection "1; mode=block"
Header set X-Frame-Options "SAMEORIGIN"
Header set Content-Security-Policy "default-src 'self' 'unsafe-inline' 'unsafe-eval' disqus.com *.disqus.com; style-src 'self' *.googleapis.com *.gstatic.com 'unsafe-inline'; font-src 'self' *.gstatic.com"
Testing
Scott has also created a site for you to be able to test your setup. Just go to https://securityheaders.io, enter your site name and get an almost immediate response.
I don’t have my cert pinned at the moment. You can also note that I
have a warning for the CSP; I allow ‘unsafe-inline’. So my configuration
isn’t necessarily safe, but because I’m forcing SSL then the chances of
an attacker injecting rogue scripts isn’t really any different to trusting
self
and having the code loaded from an external page.
Summary
Most of these headers can be turned on without any impact on your app; forcing TLS, turning off content-type sniffing, turning on XSS protections and frame clickjacking protectings are mostly safe (not 100% because a site may trip things and need workarounds). CSP is the hard one, but definitely worth spending time on.