July 26, 2016

An Introduction to Content Security Policy (CSP)

by Jscrambler

An Introduction to Content Security Policy (CSP)

As a JavaScript developer, you’re no doubt painfully aware of some of the common vulnerabilities in web applications which are made possible by the language; most notably, XSS (Cross Site Scripting) attacks.

At the root of XSS attacks is a simple premise; the injection of malicious code into your website or web application.

The first line of defense against XSS usually involves sanitizing user input, particularly anything which is later echoed back to the page. Content Security Policy is a subtly different approach to defending against similar types of attack. In this article, we’ll look at it in more detail.

Start Jscrambler Free Trial

Note that CSP is not a replacement for input sanitization, which remains as important as ever. Rather, it complements the best practices you’re already (hopefully!) following.

The Basics of CSP

Essentially CSP allows you to be explicit about what resources should be trusted. Resources mean scripts — which is what we’re going to focus on in particular in this article — but also things like:

  • stylesheets
  • media (for example audio and video)
  • fonts
  • form actions
  • frame sources
  • types of objects and plugins

In essence, what CSP provides is a means to “whitelist” the source of these resources. So we could say, for example, that we wish to allow third-party scripts from our analytics provider, from certain CDNs or social networks — but, make clear that anything else is not to be trusted. Let’s look at how to actually implement CSP in the next section.

Implementing CSP

There are two ways to implement CSP:

  1. Via HTTP headers
  2. Via meta tags in your HTML

Although they’re slightly more complex to set up than meta tags, HTTP headers are the preferred approach. There are numerous ways to accomplish this; for example setting them on your web server, by using middleware, or by setting them programmatically as required, for example on a per-route basis.

The CSP Headers and Simple CSP packages are just two JS-orientated packages which may help you to implement CSP. There are plenty more for your server-side language of choice, so it’s worth doing some research before you start implementing it yourself.

Here’s an example of setting up CSP headers in an Express.js application, using the Simple CSP package:

var express = require('express');
var app = express();
var csp = require("simple-csp");

var csp_headers = {
    "default-src": ["'self'", "http://example.com"],
    "connect-src": ["'self'", "http://example.com"],
    "img-src": ["'self'", "data:", "http://example.com"]
};

app.use("/", function(req, res, done) {
    csp.header(csp_headers, res);
    done();
});

// Static files from ./public
app.use("/", express.static("./public"));

app.listen(8888);

Right now you might be wondering what those headers mean; let’s take a look in more detail.

Whitelisting Sources with CSP

You can specify what’s trusted in a number of ways.

For example, you might want to say:

  • Trust only scripts from the same source via HTTPS, as well as from platform.twitter.com.
  • Disallow all inline scripts
  • Only allow images from a particular CDN
  • Disallow frames
  • Only allow fonts from Google Fonts

Let’s look at how to define the source of a resource. Consider the following:

example.com

This will allow resources from example.com using any scheme (e.g. http, https, data), on any port.

You can explicitly define the scheme, for example:

https://example.com

You can also explicitly define a subdomain:

https://cdn.example.com

If you want to be slightly more flexible about protocols you can do this:

*://cdn.example.com

Wildcards can also be used for the leftmost portion of the domain; for example, a subdomain:

https://*.example.com

Note that this will not match example.com

You can also explicitly specify the port:

https://example.com:443

There are also four important constants you can use.

'self' is probably the most common. It basically means that resources from the current host are to be trusted, which is usually going to be the case.
'none' is just as it implies; trust nothing. It can be used for a “safety-first” rule-set, or be applied to certain resource types; for example, you could use it to disallow all frame sources.
'unsafe-inline' allows potentially unsafe inline JavaScript. As the name implies, this requires caution – we’ll look at inline JavaScript shortly.
'unsafe-eval' permits potentially unsafe eval‘ed code. Use with even more caution!

For all of these constants, it’s important that you wrap them in single quotations, as listed.

Once you have your sources defined, you simply append them to a keyword representing the resource type. For scripts, that means using the script-src keyword.

Separate sources with a space, for example:

Content-Security-Policy: script-src 'self'
https://platform.twitter.com https://cdn.example.com

Other resources can be specified using keywords such as connect-src for XHR or web socket connections, style-src, font-src, img-src for styles, fonts and images respectively, child-src for workers and embedded frame contents, and more.

You can specify a default source for all resources using default-src, for example:

default-src 'self'
cdn.example.com

What about Inline JavaScript?

That’s all very well, you might be thinking, but aren’t inline scripts what we should really be worrying about when trying to combat XSS?

The short answer is that by CSP takes the view that all inline scripts are potentially harmful, thus encouraging you to move inline JS code to a separate file. You can tell CSP to ignore this restriction using unsafe-inline, but there are better ways.

If you do still want to execute inline code, CSP still provides two mechanisms for telling the browser what inline code is legitimate.

The first is to use a nonce; that is, a random unguessable string which is used to identify trusted inline code. It should be random, unguessable and ideally re-generated on each page load.

The first step to using the nonce approach is to attach it to your <script type="text/javascript">// <![CDATA[ tags using the nonce attribute, for example:

<script nonce="d9j8g9irjgirjheg9i">
    console.log('This code is trusted!');
    // ]]>
</script>

Then, the same value should be inserted into your CSP header or meta tag, for example:

Content-Security-Policy: script-src 'nonce-d9j8g9irjgirjheg9i'

The second approach is to generate a hash of the inline code, using an encryption mechanism such as SHA.

For example:

<script type="text/javascript">
    // <![CDATA[
    alert('Hello, world.');
    // ]]>
</script>

Take the hash of the contents of the <script type="text/javascript">// <![CDATA[ tag, and refer to it in your headers:

Content-Security-Policy: script- src 'sha256-qznLcsROx4GACP2dm0UCKCzCG-HiZ1guq6ZZDob_Tng='

Note that the first part of the hash identifies the mechanism used to encode it; in this case, SHA256

Reporting

Another key feature of the CSP specification is reporting. Indeed, it's relatively common just to use this feature; that is, as a tool to monitor potential vulnerabilities rather than as a preventative measure, although you can, of course, use both.

What the reporting element does is monitor for violations of the policy, and POSTs a report to the endpoint you specify via the report-uri keyword. Here's an example:

Content-Security-Policy: ...;
report-uri / csp-report-endpoint;

When a violation occurs, your endpoint will be "pinged" with an HTTP POST request with a JSON-formatted body. Here's an example of what such a report might look like:

{
    "csp-report": {
        "document-uri": "http://example.com/signup.html",
        "referrer": "",
        "blocked-uri": "http://example.com/css/style.css",
        "violated-directive": "style-src cdn.example.com",
        "original-policy": "default-src 'none'; style-src cdn.example.com;
                             report-uri /_/csp-reports"
    }
}

How you interpret or respond to these reports is up to you; there are third-party parsers and endpoint implementations available for various server-side languages — CSP Endpoint is just one example, for Node.js.

Browser Support

As always, it's important to be aware of browser support - you can find more details on caniuse.com.

The Future of CSP

CSP as a standard is constantly evolving. Level two support is fairly common, (see caniuse.com for full details), whereas Level Three is a work-in-progress. It might be worth keeping an eye on the public-webappsec mailing list archives to keep up-to-date with developments, as well as referring to the draft Level Three standard.

Flaws and Limitations of CSP

CSP isn't perfect, and it's easy to misuse. We'll cover CSP's limitations in a future post, but for now, you may find the notes from this presentation useful reading.

Links and Further Reading

There's a lot more to CSP than we've been able to cover in this short introduction, but if you're interested in finding out more then here are some useful links and resources:

Start Jscrambler Free Trial