Note: This post has been crossposted from the SendSafely blog. You can find the original post at http://blog.sendsafely.com/post/42277333593/using-content-security-policy-to-prevent-cross-site.
The Content Security Policy in SendSafely
Dissecting our Policy
Now let’s take a look at the CSP policy we use on www.sendsafely.com and dissect it a bit. One of the first things to note is that if you are going to implement CSP, you must realize that there are some browser compatibility nuances to deal with. The main thing to note is that Safari uses ‘X-WebKit-CSP’ as the header name for implementing CSP, while other browsers have standardized on ‘X-Content-Security-Policy’. Another glitch that affects Safari is that a severe bug in the CSP implementation on Version 5.1 essentially blocks authorized content when a valid CSP is specified. As a result, you’ll want to specifically detect when Safari is used and send either the ‘X-WebKit-CSP’ header or no header at all (if Version 5.1 is used).
To keep our policy as strict as possible, we use two different policies depending on what the page needs to do. The stricter policy is used for all pages except the ones that handle encryption and decryption (the reason for this will be discussed in a separate follow up post). For simplicity, the more strict policy will be explained here.
X-Content-Security-Policy: default-src ‘none’; connect-src ‘self’; script-src https://static.sendsafely.com https://www.google.com https://ssl.google-analytics.com; style-src ‘self’ ‘unsafe-inline’ http: https:; img-src ‘self’ https://www.google.com https://ssl.google-analytics.com; report-uri /csp-reports;
The header is divided into different sections that are each separated by a semi-colon. The “default-src” directive defines the security policy for all types of content which are not expressly called out by more specific directives. We opted to set the default-src value to ‘none’, meaning that by default we allow nothing to load. If we stopped defining directives here, the site would be completely broken, so now we need to open up the policy and allow specifically what we need to load.
Now that we’ve explicitly denied everything by default, we need to add back the specific content policy options our site needs. On SendSafely, we have a hand full of resource categories that we need to add policy settings for. Each of these are outlined below, along with the CSP directives for each:
Having such a large site like www.google.com in our CSP whitelist is understandably something we are not thrilled about. The ability to allow sub-paths of a host is slated to be introduced in CSP 1.1, but until then we’ll have to live with it. The good news is that Google takes security very seriously, and they take great care to avoid script injection bugs on their website.
- CSS - Our site design makes heavy use of in-line CSS for styling various UI attributes. As such, the style-src directive includes a value of “self” (that allows us to load CSS files from the same host) and a value of “unsafe-inline”, meaning that we can use in-line CSS from within our HTML pages. We recognize that by allowing in-line CSS within our pages, there is a minimal increased security risk since someone could potentially be mischievous if they found a way to inject markup into one of our pages. Given the cost/benefit of refactoring the UI to completely avoid any in-line CSS, however, we decided this is a tolerable risk that we can live with for now.
- Images - Our img-src directive specifies both “self” and the two previously mentioned google hosts (https://www.google.com and https://ssl.google-analytics.com) as the authorized origin hosts for all image content For the most part, our site only loads images from the same host. The exception to this is reCaptcha, however, since reCaptcha loads various images from www.google.com domain.
- The final part of our CSP header is the ‘report-uri’ directive. This directive tells the browser to send us a report of pages that violate the Content Security Policy. The violation reports consist of JSON documents sent via an HTTP POST request to the specified URI. Using this option, we can monitor for events that trigger CSP exceptions and quickly take action if we think there may be a problem with our site. The reports are also great to use during testing and development in order to debug CSP issues you might encounter.
A few final notes: CSP is a great tool to add an additional layer of protection against Cross-Site Scripting. If you’re building a new application, CSP should be considered as a solid defense in depth security control in the never-ending battle against cross-site scripting. Writing client-side code which is designed to use CSP will save precious developer cycles in the future, if code must be migrated to work with CSP.
Implementing CSP on our site proved to be a very interesting exercise. We’ll provide more details on some other aspects of our Content Security Policy implementation in a follow up post here on our blog.