I recently wrote an update as I continue to work on my response headers, in which I said that I was working on adding Content Security Policy (CSP), with the help of Scott Helme, who has written a great blog post on this. He has also created an excellent site called report-uri.com which has a number of tools, including one to help you build your CSP.
So there are lots of parts, but here’s how I went about it…
Default Source (default-src)
This is the default setting, so to be really secure, I went with “none”. This essentially means don’t allow any content at all. It is then overridden for each content type explicitly.
Script Source (script-src)
Style Source (style-src)
This is applied to all stylesheet files and style tags on the page. Again, I started with “self”, and then needed to add “https://maxcdn.bootstrapcdn.com”.
Image Source (image-src)
You guessed it, this is applied to all the images on the page. This time I needed “self” and also “https://www.gravatar.com”.
Font Source (font-src)
I don’t load any fonts from my own site here, so I just need to add “https://maxcdn.bootstrapcdn.com”.
Connect Source (connect-src)
This is applied to things like AJAX requests, web sockets and event sources. I don’t have any of these currently, so I’ve left this out, which means it will default to the Default Source, which is “none”. I could also explicitly set this to “none”.
Media Source (media-src)
This is applied to audio and video tags. I don’t have any, so I’ve left this out.
Object Source (object-src)
This is applied to object, applet and embed tags. I don’t have any, so I’ve left this out.
Child Source (child-src) / Frame Source (frame-src – deprecated)
These are applied to frame and iframe tags. I don’t have any, so I’ve left these both out. You should use Child Source instead of Frame Source though, as the later is deprecated.
Worked Source (worker-src)
This is applied to things like service workers, which I don’t have, so I’ve left this out.
Frame Ancesters (frame-ancesters)
This is applied to frame and iframe tags, stating which parents may embed a page. It replaces the “X-Frame-Options” header, but again, I’ve left it out.
Form Action (form-action)
This is applied to form tags, stating which location they can post to. I don’t have a form, so I’ve left it out. In case you’ve forgotten, this means it defaults to the “Default Source”, which is “none”.
Upgrade Insecure Requests (upgrade-insecure-requests)
My site currently runs on HTTP (non-secure), so I’ve left this out, but I’ll definitely be looking to add them in moving forwards.
Block All Mixed Content (block-all-mixed-content)
Same as above, I’ve left these out but I’ll be looking to add them in moving forwards.
Reflected Cross-Site Scripting (reflected-xss)
Reflected cross-site scripting is bad! I’ve set this to “block”. You can set this to “filter”, which means the browser will try to cleanse the script, but I think it’s safer to block it completely, if XSS is detected.
Manifest Source (manifest-src)
This is applied to manifest files, so I’ve set this to “self”, as I have a manifest for icons.
Plugin Types (plugin-type)
This tells the browser which plugins they can invoke, such as the Adobe PDF Viewer. As I don’t think my site needs any, I’ve left this out.
This directive tells the browser when (and when not) to send the referer [sic] header when fetching content from another domain. In my last blog post I decided to set the “Referrer-Policy” header to be “no-referrer-when-downgrade”, and so I’ve set this directive to match that.
Report URI (report-uri)
This is where the browser will post violation reports to, so you can keep an eye on what content is breaking. This could be because you’ve mis-configured your policy, or it could be because someone is trying to hack your site! Scott Helme’s excellent site report-uri.com will allow you to configure a URI which you can set here.
So now it’s ready for testing!
To test, instead of setting “Content-Security-Policy”, set “Content-Security-Policy-Report-Only”. This means that the browser will report all of the violations, but will continue to load the content anyway, which is perfect until you’re sure it’s right.
This is what I came up with…
default-src 'none'; script-src 'self' https://code.jquery.com https://maxcdn.bootstrapcdn.com https://cdnjs.cloudflare.com; style-src 'self' https://maxcdn.bootstrapcdn.com; img-src 'self' https://www.gravatar.com; font-src https://maxcdn.bootstrapcdn.com; upgrade-insecure-requests; block-all-mixed-content; reflected-xss block; manifest-src 'self'; referrer no-referrer-when-downgrade; report-uri https://rik.report-uri.io/r/default/csp/reportOnly;
I then reloaded my site, checked the console, and saw a number of violations!
This is because I’d forgotten a couple of included scripts that are loaded by Google Analytics. In fact, they also use an inline script tag, but I really don’t want to add “unsafe-inline” to the Script Source, otherwise all inline scripts could be run.
Luckily you can fix inline script and style tags that you want to allow by calculating a SHA-256 hash of the contents and including this in the Script Source. And Scott’s got a hash generator tool as well, so that’s easy.
Having fixed these problems, and reloading my site with no violations reported, I’m now ready to start enforcing. This means that I need to set the “Content-Security-Policy” header, and if you’re using report-uri.com then you need to update your Report URI directive as well.
Note that if you want to support IE11, you also need to set “X-Content-Security-Policy” to the same value. It’s not supported in older versions of IE at all.
Now that I’ve added these extra headers, you can check out my live report. Last I checked, I got an A+ 🙂