Using CSPs to Reduce Front-End Attack Vectors
What is a CSP?
CSP is an acronym for Content Security Policy. It can be used as a white-list of things the browser can and can’t do with a Web App or Website. A CSP can help prevent content injection vulnerabilities like Cross Site Scripting (XSS) and can be used to mitigate interception attacks like Man In The Middle (MITM).
Why Use a CSP?
If you’ve ever made AJAX or fetch requests to an API that isn’t on your domain, you’ve probably seen a Cross-Origin Request Sharing (CORS) error. This error occurs because browsers implement the Same Origin Policy. In theory,
a.com should not be able to access data from
b.com. However, in practice, this mostly works, but there are ways around the Same Origin Policy.
The best way to think of a Web App or Website is that it’s vulnerable until properly configured and includes a CSP that explicitly defines that configuration.
How Do I Add a CSP?
There are two ways to include a CSP. It can be added to your Web App or Website as an HTTP header or as a meta tag in your HTML.
As an HTTP Header
You can include the CSP in the response from your server. Here, the value of the header is the Policy Value.
Content-Security-Policy: default-src 'self' *.foo.com;
As an HTML Tag
In the case where you are serving a static site, say from an AWS S3 Bucket or from a peer-to-peer application, you may want to include the CSP as a meta tag in the document itself. In this example, the value of the
content attribute is the Policy Value.
<meta http-equiv="Content-Security-Policy" content="default-src 'self' *.foo.com;">
How Do I Write a CSP?
The grammar for writing a CSP is outlined in the spec found here, but here’s a quick primer to get you started.
The gist of the grammar for the Policy Value is that it’s a semicolon-delimited list. Each item in the list is a directive. Each directive is a key-value pair, and sometimes the value may itself be a list of values. Let’s use some actual code to illustrate.
For readability in the following code, I’ve used line breaks to separate the directives instead of the usual single whitespace character.
default-src 'none'; img-src https://a.com; child-src 'self'; connect-src https://api.a.com; script-src https://*.a.com; style-src https://a.com;
The most secure Policy Value is one that starts by blocking everything with the
default-src directive with the value of
none. After that, we can progressively white-list the things we want to allow.
If our domain is
a.com, we can allow images from that domain with the
img-src directive. For its value, using
https://a.com will ensure
https is the only protocol allowed to load images. This is the same principle for all directives.
style-src directive can prevent certain XSS based attacks such as CSS based Key Logging by controlling where CSS is loaded from. In general, inline CSS should be considered inline code, and it should be considered as potentially harmful as using
There are cases, however, where you might want to load resources from another domain, Google Fonts for example. In this case, we can add a few more attributes.
style-src 'self' https://a.com https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com;
connect-src directive decides what domains we can connect to with web sockets, fetch, and XHR APIs. So, if we also want to connect to a third-party service, we can just add another domain to this directive. Wild cards are also supported.
script-src directive specifies where code can be loaded from. In this case, we’ve used a wild card to allow scripts to be loaded from any subdomain on our domain.
Inline Code & Building Frameworks
There is almost never a case where a developer should inline code. In some cases, though as a framework author, you may want to embed some code in a document. Although inconvenient, it is possible to do this safely.
One option is with a Nonce. For example, you can have a server inject a new random string into your HTML template each time it serves your page. In this case, I’ve substituted some of the actual code with an ellipsis (for readability).
On the script’s tag, you’ll need to add an attribute that relates to the policy or the script won’t run.
<script nonce="...d29ybGQK"> // Some library code here. </script>
For nonces, it is important to note that you’ll want to leverage CSP through an HTTP Header instead of a
tag to prevent client-side modification of your HTML.
The other option is to hash the code and specify the hash in the tag. For example, you could
cat the file and pipe it to
cat mybundle.js | shasum -a 256
Then, with the output, specify the algorithm used and the output of the hash (separated by a dash).
Hashing your code still doesn’t make it safe to use
eval or pass a string to the
Function() constructor. Both are considered unsafe in almost all cases and are discouraged.
For more information about the specific directives you can use this; it is the official spec. For an easy to read list of directives and their possible values, use mdn, but for browser compatibility, Can I Use is also a great reference.
Everyone should be using a CSP. Be sure that your CSP only permits the browser to do what you actually need it to do. Fewer moving parts means less risk. Less surface area means fewer attacks.