# Angular Content-Security-Policy Complex Nonce: Google Tag Manager

Web applications get exploited, leading to economic and reputation damage. Rich content is difficult to protect. Complex standards and complex tooling fight with each other. New technologies like Angular Single Page Applications and externally-driven analytics make it difficult to construct a valid Content-Security-Policy. If you get this wrong, you get malware injected on your site, as I wrote about [here](https://www.agilicus.com/stop-injected-malware-on-your-sit/). Today let's learn how, if we must have an inline script, we can do so with content-security-policy complex nonce

Setting up Google Analytics (via Google Tag Manager) is difficult to understand and achieve securely: you are running Javascript fetched from an inline script, how do you cause it to be trusted without destroying all trust? The answer: a **nonce**. Yes, content-security-policy is complex without the nonce. Set the nonce (to a unique value) on each page load, and use this to indicate what scripts your page has requested.

I've [talked](/project/web-security-101/) earlier about the complexities of web security, about how hard it is to balance security and functionality. One of the tools that Content-Security-Policy allows is the **Nonce**. The **Nonce** must be set differently on each HTTP response, making it complex: it requires participation of the server.

In an Angular project, we normally use **-aot** and **-subresource-integrity**, this sets a secure hash on each resource that we build and serve. However, anything that is fetched externally (e.g. Google Analytics) is more challenging. The recommended way of using Google Analytics is via Google Tag Manager. In turn, you must use a Nonce with it (as shown [here](https://developers.google.com/tag-manager/web/csp)). How can we set that Nonce to be unique, on each request, in an Angular SPA? Read on!

Before we start adding the nonce to external scripts, lets ensure we have hash-based subresource integrity enabled for all internal, compiled Typescript:

```
ng build --aot --subresource-integrity --outputHashing=all --prod=true
```

If we look at our index.html in the dist directory, we will now see it looks something like:

```
...
<link 
 rel="stylesheet" 
 href="styles.00fa916e08ff7ba62ff7.css" 
 crossorigin="anonymous" 
 integrity="sha384-+tmFFHbEXmmdzqySTeVvrRc2gEDLUMX3aMSwQxwB0spQcDT4EM2T6uHhCQ840BVW"/>
</head>

<body>
...
<script 
 src="runtime.7b63b9fd40098a2e8207.js" 
 crossorigin="anonymous" 
 defer 
 integrity="sha384-l94orWp9/B8nJYxIHArstzxjyopeYkvuFAGAhd+P3juqcmrB3UHmRx4r1Ib76hcW">
</script>
...
```

Those 'integrity=' lines have a sha384 hash of the body of the file. You can read [more](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity) about Subresource Integrity. These hashes were generated by Webpack as part of the angular build, in essence, for each file, it performed:

```
openssl dgst -sha384 -binary XXXX.js | openssl base64 -A
```

and this means your browser won't load the resulting file if it has been modified. But, that is not what we are here for today, we are here for external resources (e.g. Google Tag Manager), and a safe way to allow them to be loaded, the nonce.

Next, we enable **[indexTransform](https://github.com/just-jeb/angular-builders/blob/master/packages/custom-webpack/README.md)**. We cause it to, on production builds, add a script to the index.html HEAD section. We add a magic string CSP\_NONCE which we will then replace in the server side (using lua in nginx).

```
npm i -D @angular-builders/custom-webpack
```

```
/*
 * index-html.transform.ts
 * This exists to modify index.html, after build, for prod,
 * to insert the google tag manager
 */
import { TargetOptions } from '@angular-builders/custom-webpack';
import { environment } from './src/environments/environment.prod';

export default (targetOptions: TargetOptions, indexHtml: string) => {
  let insertTag = `<!-- No script for gtm, non-prod -->`;
  if (targetOptions.configuration || 'production') {
    insertTag = `<script nonce=CSP_NONCE src=https://www.googletagmanager.com/gtm.js?id=${environment.gtmTag} async></script>`;
  }

  const i = indexHtml.indexOf('</head>');
  return `${indexHtml.slice(0, i)}
            ${insertTag}
            ${indexHtml.slice(i)}`;
};

```

```
// src/environments/environment.prod.ts
export const environment = {
...
  gtmTag: 'GTM-XXXXX',
...
};
```

```
// angular.json
{
...
      "architect": {
        "build": {
          "builder": "@angular-builders/custom-webpack:browser",
          "options": {
            "aot": true,
            "outputPath": "dist",
            "index": "src/index.html",
            "indexTransform": "./index-html.transform.ts",
...
        "serve": {
          "builder": "@angular-builders/custom-webpack:dev-server",

```

OK, no we are generating an Angular SPA with a header which loads our Google Tag Manager, with our tag, and a **Nonce** just waiting to be set. Note: you don't have to use the Webpack transform, you can just hard code in the script line with the **&lt;script nonce=CSP\_NONCE** prefix if you prefer.

We will now replace the string nonce=CSP\_NONCE with a new, per transaction value, in our *nginx.conf*, adding a new location /index.html which will find all CSP\_NONCE in scripts, and alter:

```
location /index.html {

    set_by_lua_block $cspNonce  {
        local base64 = require('ngx.base64')
        local file = assert(io.open('/dev/urandom', 'rb'))
        local bytes = file:read(32)
        file:close()
        return base64.encode_base64url(bytes)
    }

    sub_filter_once off;
    sub_filter_types text/html;
    sub_filter_last_modified on;
    sub_filter '<script nonce=CSP_NONCE' '<script nonce="$cspNonce"';

    try_files $uri $uri/ /index.html;
}
```

Now, when we fetch our index.html, the string is replaced on each transaction.

The net affect of this is we can safely use Google Tag Manager, since it, and its chain of dependants, could only have been fetched via our code. If we have a flaw in our application, perhaps incorrectly sanitising user-generated content, it will be unable to fetch a script (since it cannot guess our **Nonce**). Give it a try! No more '\*' and 'unsafe-\*' for Content-Security-Policy, there's no need.

Make sure you have a Content-Security-Policy header set (you can use nginx add\_header to set it) to a strong policy.

Now, lets test. My favourite tool is the [Mozilla Observatory](https://observatory.mozilla.org/). Enter your URL, and let it scan. It will come back with a very actionable list for you. Below is an example output showing the results after we have added content-security-policy complex nonce.

[![Angular Content Security Policy (CSP) for Google Tag Manager (GTM) - Agilicus.com: Secure your Angular application by implementing a strict CSP that allows Google Tag Manager to function correctly. This includes configuring script-src, img-src, and connect-src directives to whitelist Google Tag Manager and Google Analytics domains, preventing security vulnerabilities like cross-site scripting (XSS) attacks. Agilicus provides solutions for robust web application security.](https://www.agilicus.com/www/2b2aa5fd-image.png)](https://www.agilicus.com/www/2b2aa5fd-image.png)