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:
Via HTTP headers
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">// <