Content Security Policy - An Introduction
Addy Stark 

Content Security Policy is delivered via a HTTP response header, much like HSTS, and defines approved sources of content that the browser may load. It can be an effective countermeasure to Cross Site Scripting (XSS) attacks and is also widely supported and usually easily deployed.


Why do we need CSP?
When your browser loaded this page, it loaded a lot of other assets along with it. There are stylesheets and fonts to load along with quite a few javascript files. One for the Disqus comment system, one for Google Analytics, my social sharing buttons down at the bottom and a few more for good measure too. Your browser loads these assets because it is instructed to do so by the source code of the page. It has no way of knowing whether or not any of those files should or should not be loaded. An attacker could have placed a specially crafted comment in the comment section to load some malicious javascript from a 3rd party domain and this would also be loaded by your browser because it was included along with the page. Your browser has no reason to not trust the content from nastyhackers.com and no way of knowing the content is malicious. This is where CSP comes in.


Whitelisting Approved Sources
A CSP header allows you to define approved sources for content on your site that the browser can load. By specifying only those sources that you wish the browser to load content from, you can protect your visitors from a whole range of issues. Here is a basic CSP response header.

Content-Security-Policy: script-src 'self'


Going back to the example above of an attacker using a specially crafted comment to load javascript from another domain, this CSP header would prevent the browser loading content from nastyhackers.com. The script-src directive specifies the whitelist of sources that the browser may load scripts from. Using the 'self' keyword is easier than specifying my whole domain and makes the policy a little easier to read once it starts growing. Because the domain nastyhackers.com isn't in the script whitelist, the browser will not load the script content from nastyhackers.com.


What can we protect?
CSP comes with a wide range of directives that can be used to enforce policy across a whole load of content and circumstances. Here is a list of all of the directives that are available, along with a brief description, courtesy of OWASP.


------------------------------------------------------------------------------------------default-src: Define loading policy for all resources type in case of a resource type dedicated directive is not defined (fallback),
script-src: Define which scripts the protected resource can execute,
object-src: Define from where the protected resource can load plugins,
style-src: Define which styles (CSS) the user applies to the protected resource,
img-src: Define from where the protected resource can load images,
media-src: Define from where the protected resource can load video and audio,
frame-src: Define from where the protected resource can embed frames,
font-src: Define from where the protected resource can load fonts,
connect-src: Define which URIs the protected resource can load using script interfaces,
form-action: Define which URIs can be used as the action of HTML form elements,
sandbox: Specifies an HTML sandbox policy that the user agent applies to the protected resource,
script-nonce: Define script execution by requiring the presence of the specified nonce on script elements,
plugin-types: Define the set of plugins that can be invoked by the protected resource by limiting the types of resources that can be embedded,
reflected-xss: Instructs a user agent to activate or deactivate any heuristics used to filter or block reflected cross-site scripting attacks, equivalent to the effects of the non-standard X-XSS-Protection header,
report-uri: Specifies a URI to which the user agent sends reports about policy violation


---------------------------------------------------------------------------

otthelme.co.uk would result in no scripts being loaded over https from scotthelme.co.uk. The correct policy would be:


Content-Security-Policy: default-src https:; script-src https://scotthelme.co.uk http://scotthelme.co.uk


Testing A Policy
Once you've created your policy, there's a really great feature you can take advantage of to test it. Instead of sending the header Content-Security-Policy:, you can send Content-Security-Policy-Report-Only:. This means the browser will receive and act upon the policy, but instead of enforcing it, it will give you feedback on what the effects of the policy would have been.



Now you can deploy, test and fine tune your policy without risking a disaster like accidentally blocking all JS on the page! Another great feature to take advantage of at this point is the ability to send both headers. Once you're happy with your policy and are sending the Content-Security-Policy: header, there will no doubt come a time when you want to make changes to that policy. What you can do is implement the changes you need and send the new policy in the Content-Security-Policy-Report-Only: header. The browser will continue to enforce the existing policy but you can also test and get feedback on the changes from the new policy without having any negative impact. I should probably also mention that CSP is only enforced on a per page basis. The browser will not cache a CSP header and continue to enforce it so you must send the header with every response that you want the policy to be enforced on.


Reporting Violations
It's all good and well having these policies in place, but it would be even better if we knew when the browser was taking action based on them. Perhaps an asset is being referenced in production that shouldn't be, or perhaps an attacker has found an XSS attack vector and is actively trying to exploit it. Without any form of active reporting, we have no way of knowing what's happening. For this, we have the report-uri directive. This directive specifies a location that the browser should POST a JSON formatted violation report to in the event it has to take action based on the CSP.


If I used a basic policy on my site to only allow assets to be loaded over https, but a reference to a http asset managed to find a way in, the browser would block the asset from being loaded and deliver a report to me.


The policy: Content-Security-Policy: default-src https:; report-uri https://report.scotthelme.co.uk


The offending item: <img src="http://scotthelme.co.uk/logo.png">


The resulting report:

{
    "csp-report": {
        "document-uri": "https://scotthelme.co.uk",
        "referrer": "",
        "blocked-uri": "http://scotthelme.co.uk",
        "violated-directive": "default-src https:",
        "original-policy": "default-src https:; report-uri https://report.scotthelme.co.uk",
    }
}



Any inline script on the page that you wanted to be executed would use that value like so:


<script nonce="SomeRandomNonce">...</script>


This method is more suited to pages that are generated dynamically and requires the use of a sufficiently random and long nonce to prevent an attacker predicting the value for their own scripts. You can use the same nonce value for every script on the page, but it has to be re-generated and inserted into the header and all script elements on each response. A static nonce value would be useless.


Hash
The next method is to place a hash of the script or style in the CSP header. More suited to static content, the browser will hash any inline JS or CSS and see if the digest matches a value found in the header. If it does, the content is safe for use.


Content-Security-Policy: script-src 'sha256-HashDigestHere='


The contents of any <script> tags on the page would then be hashed and compared to the value found in the CSP header. If the values matched, the script would be allowed to execute.


The Problem
As both of these scripts load 3rd party content, I can't go down the hash route as everything would break as soon as they made the tiniest change to any of the content. This leaves me with the nonce approach. Being quite difficult to implement and maintain, I'm effectively left with the choice of not having adverts and Disqus, or reducing the constraints the policy applies. For the time being, that effectively leaves me with the following policy:


Content-Security-Policy-Report-Only: default-src https: 'unsafe-inline' 'unsafe-eval'


All this does is ensure that all content on the page will be loaded over a secure connection, or not loaded at all, preventing any mixed content warnings. It doesn't offer any protection against XSS. I think this highlights some of the difficulties of implementing CSP and that building a site with CSP in mind from the beginning would be much better. A lot of the requirements, like externalising JS and CSS, are good to implement anyway, outside of the need for CSP. For now though, whilst I have a CSP in place, I would have much preferred something more secure.


Browser Support
Fortunately, support for the CSP header is widespread but there is one thing to watch out for, good old Internet Explorer. The Content-Security-Policy header is supported in the latest and greatest versions of Chrome, FireFox, Safari (OSX and iOS), Opera (but not Mini), the Android Browser and Chrome for Android. Internet Explorer, however, requires the X-Content-Security-Policy header instead. This means that if you want to have the most widespread support for your CSP header, you will need to issue it twice! I have to admit I'm not a great fan of that prospect but hopefully that will change in IE 12.