Dotting the i and crossing the t for your website
Recently I did an overhaul of my website, which was long over due. After spending some time creating a new WordPress theme and adding the first content, I moved on to make sure it would show in Googles search results. Following the advice given by Google I changed my theme for the better. Simple and small changes like a consistent, but dynamic title on the different pages.
Content with the setup I let it be to start focussing on some content. However, a good friend of mine suggested the observatory project by Mozilla. Being familiar with OWASP I loved to have a tool that independently would scan my website and give advise on some improvement points.
Obviously most of the points were to remove some shortcuts taken during development:
- No inline styling; use the stylesheet, that’s what its for!
- No inline scripts; move them to a separate file.
Others are simple additions to your site that help making the internet a little bit more secure:
- Strict protocol usage. If you support `https://`, which you should, make sure all your visitors know this and will move to use only `https://`. Besides configuring WordPress to use the right URL for your site, adding the `Strict-Transport-Security` header to your responses will make sure your visitors won’t forget it.
- Don’t allow your site to be wrapped inside a frame. Add the `X-Frame-Options` header to prevent this.
I moved on to work on the `Content-Security-Policy`, which is
an HTTP header that allows site operators fine-grained control over where resources on their site can be loaded from. The use of this header is the best method to prevent cross-site scripting (XSS) vulnerabilities.
In short your website should provide a strict list of external resources that may be loaded by your website. Building this list of external resources was a simple but tedious exercise. You can even cover those small scripts that WordPress automatically adds to your pages by calculating a SHA hash value for it.
All went fine until I wanted to enable Subresource Integrity (SRI). This recent addition to W3C standards allows you to provide an integrity tag on any resource your using. Specifically useful when your using CDNs to provide commons resources, but you want to make sure your website won’t be compromised when the CDN is. A precondition for this all to work is that the CDN must allow Cross Origin Resource Sharing (CORS) for that resource. You would think that all third party script providers would have this enabled by default, as this is kind of their main business and fortunately, most have already enabled this. Hence it was no trouble enabling SRI for Bootstrap or JQuery. My problem started when I wanted to do the same for Google Analytics.
Obviously I’d like to get some statistics on how often my site gets visited and Google Analytics seems like the simple choice.
I started my efforts by calculating a hash value for the resource using the following command:
> curl -s https://www.googletagmanager.com/gtag/js |\
openssl dgst -sha384 -binary |\
openssl base64 -A
And I added the result as a integrity attribute to the script tag. Together with the `crossorigin` attribute set to `anonymous` this should protect the integrity of my website’s (Sub)Resource.
Checking my website however, showed me something completely different. In the console a big error drew my attention:
Subresource Integrity: The resource 'https://www.googletagmanager.com/gtag/js' has an integrity attribute, but the resource requires the request to be CORS enabled to check the integrity, and it is not. The resource has been blocked because the integrity cannot be enforced.
Somehow Google has forgotten to change the `Access-Control-Allow-Origin` header for both the new www.googletagmanager.com as well as the old www.google-analytics.com.
I’ve asked around on StackOverflow and on the analytics help forum. I even filed a feedback form on the analytics website, but until now no solution has been found how to combine Google’s gtag.js and SubResource Integrity.
- Hosting the file your self; Although possible, Google explicitly discourage this.
- Get Google to change the CORS header to allow cross origin resource sharing.
- Get Google to host the file on a proper CDN.