An Introduction to Content Security Policy (CSP)
July 26th, 2016 | By Jscrambler | 7 min read
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.
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 “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 actually 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 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.
Allowlisting 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:
The Level Two Candidate Recommendation is essential reading.
The Level Three Editor's Draft is worth keeping an eye on.
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