A couple of weeks ago, I gave a small talk on the Madeira Tech Meetup about a set of HTTP headers that could help website owners protect their assets and their users. The slides are available here, just in case you want to take a look.
The content of the talk is basically a small review about what exists, what each header tries to achieve and how could you use it.
After the talk I remembered that I didn’t review the heades of this blog for quite sometime. So a quick visit to Mozilla Observatory, a tool that lets you have a quick look of some of the security configurations of your website, gave me an idea of what I needed to improve. This was the result:
So what is a Content Security Policy? On the MDN documentation we can find the following description:
The HTTP Content-Security-Policy response header allows web site administrators to control resources the user agent is allowed to load for a given page.
Mozilla Developer Network
Summing up, in this header we describe with a certain level of detail the sources from where each type of content can be fetched in order to be allowed and included on a given page/app. The main goal of this type of policy is to mitigate Cross-Site Scripting attacks.
In order to start building a CSP for this blog a good approach, in my humble opinion, is to start with the more basic and restrictive policy and then proceed evaluating the need for exceptions and only add them when strictly necessary. So here is my first attempt:
default-src 'self'; object-src 'none'; report-uri https://ovalerio.report-uri.com/r/d/csp/reportOnly
Lets interpret what it says:
- default-src: This is the default value for all non-mentioned directives.
self
means “only things that come from this domain”. - object-src: No <object>, <embed> or <applet> here.
- report-uri: All policy violations should be reported by the browser to this URL.
The idea was that all styles, scripts and images should be served by this domain, anything external should be blocked. This will also block inline scripts, styles and data
images, which are considered unsafe. If for some reason I need to allow this on the blog I could use unsafe-inline
, eval
and data:
on the directive’s definition but in my opinion they should be avoided.
Now a good way to find out how this policy will affect the website and to understand how it needs to be tuned (or the website changed) we can activate it using the “report only mode:
Content-Security-Policy-Report-Only: <policy>
This mode will generate some reports when you (and other users) navigate through the website, they will be printed on the browser’s console and sent to the defined report-uri
, but the resources will be loaded anyway.
Here are some results:
As an example below is a raw report from one of those violations:
{
"csp-report": {
"blocked-uri": "inline",
"document-uri": "https://blog.ovalerio.net/",
"original-policy": "default-src 'self'; object-src 'none'; report-uri https://ovalerio.report-uri.com/r/d/csp/reportOnly",
"violated-directive": "default-src"
}
}
After a while I found that:
- The theme used on this blog used some
data:
fonts - Several inline scripts were being loaded
- Many inline styles were also being used
- I have some demos that load content from
asciinema.org
- I often share some videos from Youtube, so I need to allow
iframes
from that domain - Some older posts also embeded from other websites (such as soundcloud)
So for the blog to work fine with the CSP being enforced, I either had to include some exceptions or fix errors. After evaluating the attack surface and the work required to make the changes I ended up with the following policy:
Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self' https://asciinema.org 'sha256-A+5+D7+YGeNGrYcTyNB4LNGYdWr35XshEdH/tqROujM=' 'sha256-2N2eS+4Cy0nFISF8T0QGez36fUJfaY+o6QBWxTUYiHc=' 'sha256-AJyUt7CSSRW+BeuiusXDXezlE1Wv2tkQgT5pCnpoL+w=' 'sha256-n3qH1zzzTNXXbWAKXOMmrBzjKgIQZ7G7UFh/pIixNEQ='; style-src 'self' 'sha256-MyyabzyHEWp8TS5S1nthEJ4uLnqD1s3X+OTsB8jcaas=' 'sha256-OyKg6OHgnmapAcgq002yGA58wB21FOR7EcTwPWSs54E='; font-src 'self' data:; img-src 'self' https://secure.gravatar.com; frame-src 'self' https://www.youtube.com https://asciinema.org; object-src 'none'; report-uri https://ovalerio.report-uri.com/r/d/csp/reportOnly
A lot more complex than I initially expected it to be, but it’s one of the drawbacks of using a “pre-built” theme on a platform that I didn’t develop. I was able (in the available time) to fix some stuff but fixing everything would take a lot more work.
All those sha-256
hashes were added to only allow certain inline scripts and styles without allowing everything using unsafe-inline
.
Perhaps in the future I will be able to change to a saner theme/platform, but for the time being this Content-Security-Policy will do the job.
I started enforcing it (by changing Content-Security-Policy-Report-Only
to Content-Security-Policy
) just before publishing this blog post, so if anything is broken please let me know.
I hope this post has been helpful to you and if you didn’t yet implement this header you should give it a try, it might take some effort (depending on the use case) but in the long run I believe it is totally worth it.
One reply on “Setting up a Content-Security-Policy”
Great content! Super high-quality! Keep it up! 🙂