Content Security Policy is a useful security addition to your web application but can be tricky to get started setting up. Until now.
There are some great resources out there about creating a Content Security Policy for your website but we haven't really found a good tool for generating an initial CSP for an existing web application. We wanted something that could go through our existing site and build a policy based on the current script, media, etc that we use today.
From that initial policy we could perform a proper audit, make any changes we feel were necessary, test and put live.
In an ideal world there'd be an input box on a web page somewhere that you stick your URL in and it goes off and does its magic. Unfortunately this is not really possible in the way you might imagine as that third party website simply cannot interact with the DOM sufficiently to identify all sources due to the Same Origin Policy.
Option 1 - The messy way
This actually worked pretty well but with two major flaws, you had to do this for every page on the site to ensure you covered it all and then concatenate the strings together, removing duplicates, etc. It also didn't handle the common situation where script is sourced remotely and then that script requests other remote resources. It was messy.
Option 2 - The nice way
Content Security Policy includes the option to specify a report-uri location. If this is specified in the CSP header, when a violation occurs an HTTP POST request is made by your browser to the target URL with a JSON object containing details of the violation.
Everything we needed to write a policy was staring us in the face. It's almost like the RFC had thought of this. ;-)
4ARMED has developed a working proof-of-concept that receives these violation reports, stores them in a database and then, when requested, builds a policy based off violations for a given hostname.
Because we're nice people the whole thing is open source (and we welcome pull requests for improvements) and it is also available on Docker Hub so you can fire up your own copy in Docker somewhere in just a few keystrokes.
Enough already, show me it!!
The idea is very simple, configure a restrictive, report-only CSP and set the report-uri to an instance of our CSP Generator backend. The backend consists of a simple Ruby app that uses Sinatra to serve two URLs, /report and /policy. The /report URL should be the target for report-uri. /policy is used to generate the policy for your specified hostname.
An example HTTP response header would be:
Content-Security-Policy-Report-Only: default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; report-uri http://localhost:4567/report;
In the example above it assumes the CSP generator is running on the localhost at its default port. As it's plain HTTP it won't work if you want to build a policy for an HTTPS site.
Generate the violations
With the above CSP header set on your site, simply browse the site and let it report all the violations that occur to your CSP generator backend. Of course, a better idea potentially, is to host an instance on the Internet and let your normal traffic take care of recording all the violations.
Not all people on the Internet are nice. We've already seen a fair amount of what look like report-uri spam requests. Basically peeps trying to get you to click their links when you investigate an apparent violation. There is no authentication on the report-uri (ok, there are ways but in this case, there isn't) so don't blindly trust the information recorded. Check it before using it.
We also don't recommend you leaving this service running indefinitely. It is designed to help build the CSP, not to form part of your ongoing violation reporting. There are other services available for that (hat tip https://report-uri.io).
Generate a new Content Security Policy
When you are done, simply visit your backend and access the /policy url with a RESTful argument of the hostname you are generating the policy for.
For example, if you were building a CSP for www.bbc.co.uk, you would go to http://localhost:4567/policy/www.bbc.co.uk. This will spit out a JSON response with your CSP header value under the policy key.
By default, CSP Generator will not output unsafe-inline or unsafe-eval. If you need this, specify the ?unsafe=1 parameter to the request above:
If you want to see a real life example, we have a running instance of this app at https://csp.4armed.io with a test page at https://csp.4armed.io/csptest.html. The following policy was generated for this page:
In our experimenting we found that CSP Generator covered on average over 90% of the CSP configuration needed. In fact, the only things we've found we have to manually configure so far are hash or nonce values if applicable. We're still fine-tuning the CSP for this www.4armed.com site. Anyone who runs a WordPress site will appreciate just how much of a PITA it is for inline script and CSS. We've already refactored big chunks of it and ditched half the analytics we were(n't) using. It's getting there but it's a work in progress.
Incidentally Google Analytics is the worst as we found every country-specific google.x domain ended up violating our CSP. There is no way (for obvious reasons) to wildcard the right hand side of a hostname so unless we created a CSP allowing script for every possible country-specific Google domain it was simpler to just remove GA from the site.
If you want to fire up your own instance of the server you can run the Sinatra app locally. Full details on this are provided at https://github.com/4armed/csp-backend. Alternatively, the Docker Compose setup is probably the easiest route to go. Full details on this are also on Github, this time at https://github.com/4armed/csp-generator.
Making CSP development even sweeter
Setting and re-setting CSP HTTP response headers on your websites can be cumbersome and is not without risk of causing issues for legitimate users while you experiment. So we made this step easy too with our Google Chrome Extension for CSP Generation.
You can download this from our Github at https://github.com/4armed/csp-generator-extension and this provides an easy way to interact with our CSP Generator backend plus edit and test policies on the fly right there in your browser. The real website is never affected as it is intercepting the HTTP response and manipulating the headers locally to you. It looks like this:
Once you have a CSP that works in full "ON" mode you can then implement the headers on your real web application and see how it performs for everyone.
We made the following video to give a quick demonstration of installing and using the Chrome Extension. There are full installation instructions on the Github page.
This is a quick post to release these new tools which we hope will significantly help in getting Content Security Policy enabled on more websites. We welcome feedback and more importantly, we welcome pull requests with updates!
It's alpha software and is likely to contain bugs, if you spot one please raise an issue on the Github repo. Feature requests can also go on there and we'll see what we can do.
We hope you find it useful.