DEV Community

Robertino
Robertino

Posted on • Originally published at auth0.com

Deploying CSP in Single Page Applications

Single Page Applications clash with modern CSP features. In this article, we discuss concrete strategies for securing SPAs with CSP.


The recommended best practice for modern CSP policies relies on hashes, nonces, and 'strict-dynamic'. Unfortunately, these features conflict with modern SPAs. This article proposes three concrete strategies to deploy secure CSP policies for SPAs. The first relies on a simple policy allowing 'self', which is acceptable under certain circumstances. The second strategy enables CSP hashes by rewriting the main page to use a script loader. The third strategy inserts nonces into a dynamically served main page.

Recapping CSP

The first article in this series offered an in-depth look at using CSP as a second line of defense against XSS attacks.

We covered the configuration of CSP using URL-based entries but also highlighted why such policies are often insecure. In response, we discussed Google's "universal CSP policy", shown below, which offers an excellent trade-off between security and complexity:

Content-Security-Policy: 
  script-src 'report-sample' 'nonce-3YCIqzKGd5cxaIoTibrW/A' 'unsafe-inline'
             'strict-dynamic' https: http: 'unsafe-eval';
  object-src 'none';
  base-uri 'self';
  report-uri /webchat/_/cspreport
Enter fullscreen mode Exit fullscreen mode

This policy relies on nonces to identify an initial set of legitimate script blocks and script files in the page. Once these scripts execute, they can rely on the automatic trust propagation mechanism of 'strict-dynamic' to load additional scripts.

Unfortunately, this type of policy does not work well with Single Page Applications, as we pointed out in the conclusion of the first article.

So, what is up with that? Let's take a look at the challenges of configuring CSP in SPAs and discuss a couple of solutions.

The Challenges with SPAs

Single Page Applications load a single index.html, which then bootstraps the necessary JavaScript code to launch the application. The code snippet below shows the index.html page of an Angular application:

<!doctype html>
<html lang="en">
<head>
  ...
</head>
<body>
  <app-root></app-root>
  <script src="runtime.7b63b9fd40098a2e8207.js" defer></script>
  <script src="polyfills.00096ed7d93ed26ee6df.js" defer></script>
  <script src="main.8e56a2a77fee2657fb91.js" defer></script>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

To deploy CSP in a SPA, we first have to tell the browser that the application's JavaScript bundle is a legitimate resource that can be loaded. In the snippet above, the bundle consists of three separate JavaScript files.

One way to do that is by approving scripts coming from the application's origin (https://example.com). A policy with a script-src https://example.com directive would allow the loading of these files.

Unfortunately, as we discussed in the previous article, such URL-based policies are often insecure and are deprecated. Additionally, URL-based expressions cannot be used together with 'strict-dynamic', which prevents the use of automatic trust propagation.

One alternative is the use of hashes, a CSP Level 2 feature. However, the application's JavaScript bundle is hosted as a remote file, and hashes only work on inline code blocks. So CSP hashes are not compatible with SPAs.

CSP Level 2 also supports nonces, which are compatible with the loading of remote resources. However, one requirement for nonces is that they are unique on every page load. In essence, this means that the server has to inject a fresh nonce every time it serves a page. Doing so is easy for dynamic server-side applications but not very compatible with serving a static index.html file for a SPA. So nonces are also not compatible with SPAs.

With both hashes and nonces out, there is no straightforward way for SPAs to use modern CSP policies. Additionally, they are incapable of using 'strict-dynamic' for loading additional scripts. The lack of support for 'strict-dynamic' makes it impossible to reliably incorporate third-party components such as a Twitter timeline.

Right about now, I'm sure you're somewhat disappointed with CSP, and righteously so. But don't worry, we're only getting started. Let's take a look at three concrete strategies to implement CSP in a SPA.

Read more...

Top comments (0)