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">// <![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 ArticlesMust read next
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
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