Web Security

An Introduction to Content Security Policy (CSP)

July 26th, 2016 | By Jscrambler | 7 min read

As a JavaScript developer, CSP is a mandatory concept. You know some common vulnerabilities in web applications made possible by the language, most notably, XSS (cross-site scripting) attacks.

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

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

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

The Basics of CSP

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 “allowlist” 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 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 a per-route basis.

The CSP Headers and Simple CSP packages are just two JS-orientated packages that may help you 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);


You might be wondering what those headers mean; let’s look.

Allowlisting Sources with CSP

You can specify what’s trusted in several 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 to use 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 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 it with even more caution!

For all of these constants, you must 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 be worrying about when trying to combat XSS?

The short answer is that 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 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 that 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.

The Future of CSP

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

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:

  • HTML5Rocks has a comprehensive tutorial on CSP.

  • Chrome users might find the CSP Tester extension useful for testing your CSP rules.

  • There's a great presentation, Making CSP Great Again which looks at some of the common pitfalls of CSP, as well as a look to the future.

Jscrambler

The leader in client-side Web security. With Jscrambler, JavaScript applications become self-defensive and capable of detecting and blocking client-side attacks like Magecart.

View All Articles

Must read next

Cybersecurity

CSP & Magecart Web Skimmers: Facts and Fiction

In this post, we'll explore the facts and fiction behind the topic of Content Security Policy (CSP) and Magecart.

October 22, 2021 | By Pedro Fortuna | 4 min read

Web Security

Keeping OTT Content Secure: Why Is DRM Not Enough?

In this third chapter of our series on piracy in OTT, we explore one of the biggest pitfalls of DRM and why watermarking is a much needed anti-piracy layer.

May 8, 2020 | By Jscrambler | 3 min read

Section Divider