man sitting in front of screens

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 Manipulator 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 W3.org specs, but here's a quick primer to get you started.

Grammar

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.

Directives

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. The 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 eval. 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;

The 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. connect-src https://api.a.com;The 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.script-src https://*.a.com;

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).script-src nonce-...d29ybGQK;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 shasum.cat mybundle.js | shasum -a 256Then, with the output, specify the algorithm used and the output of the hash (separated by a dash).script-src sha256-...a1ec0fb85d299a192a447Hashing 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.

Conclusion

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.

Notes

I recently noticed that CSPs don't yet handle setting rel=noopener on links. However, there is a fix that can be added to your html attributes to prevent this attack.

More from Our Cybersecurity Experts