Running this site via the excellent Security Headers security tool results in the following report summary:

Initial Security Report Summary

An A rating is pretty good, putting it within the top 25% receiving that rating. I would rather achieve the A+ rating, putting it within the top 3% 😁

What is missing is a Content-Security-Policy header, so let us see what can be done.

What is it?

Not being a front-end web engineer I was not immediately familiar with this initiative, so embarked on research into the details. Good starting places are:

To quote Scott’s article:

Content Security Policy is delivered via an HTTP response header, much like HSTS, and defines approved sources of content that the browser may load. It can be an effective countermeasure to Cross Site Scripting (XSS) attacks and is also widely supported and usually easily deployed.

Setting the header

As I am using nginx to serve my static site the answer to this is pretty straightforward. In my custom nginx.conf I can add an additional header using:

add_header Content-Security-Policy "<policy>";

The big question is what should the <policy> be?

How to determine the policy?

This site is generated using Hugo, which I imagined would be ‘doing the right thing’. Ergo, I figured a good starting place would be to define the following Content-Security-Policy:

add_header Content-Security-Policy "default-src 'self'";

Opening the site in Chrome with the developer tools, showed the following errors on the homepage:

Chrome Console Errors at root

The second error is occuring because the following <script> is not being allowed to execute, due to the CSP:

<script>let haveHeader = false;</script>

As suggested by the error message, I added the hash to the CSP for this particular script:

add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'sha256-mzxFK7/AUlIhalqxiKcyRO9mSXWeALlmGjRumxphi9M='";

Refreshing the page fixed these errors 👍🏻. Next, I visited about me and got 3 more errors in the console:

Chrome Console Errors on About Me

Interestingly, the latter two errors are caused by a very similar script as above. However, this time haveHeader is being set to true. Surely enough added the suggested hash to the CSP resolved these.

Unfortunately, adding the suggested hash for the style-src did not resolve that error. I am unsure why, so I have temporarily gone with the second suggestion of adding unsafe-inline. Navigating around the rest of the site did not result in any more console errors due to blocked content. This leaves us with an initial Content Security Policy of:

add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'sha256-mzxFK7/AUlIhalqxiKcyRO9mSXWeALlmGjRumxphi9M=' 'sha256-AnD4MU8ryLQfUOxyB+K8iDV82R0rMK6os4wCSP8Cqqo='; style-src 'self' 'unsafe-inline';";

Wrapping up

Having gone through this exercise, let’s re-run the security header check and see what the rating now is:

Latest Security Report Summary

An A+ rating - excellent… This earns us a spot in the ‘Hall of Fame’, albeit temporarily:

Security Headers Hall of Fame

Obviously should the scripts for setting hasHeader change this CSP will break. In addition, I am not entirely thrilled by having unsafe-inline for style-src. Therefore, I will reach out to the author of the theme I am using to find out if there is a better way™

UPDATE: I have raised #22: Content Security Policy on GitHub with Track3, the theme author, in case I am missing some Hugo magic.

UPDATE 2: Track3 updated his theme very quickly, removing the need for any of these workarounds - thanks. The site’s Content-Security-Policy is now:

add_header Content-Security-Policy "default-src 'self';";