DEV Community

Cover image for Using Google Tag Manager with a Content-Security-Policy
Matija Mrkaic
Matija Mrkaic

Posted on

Using Google Tag Manager with a Content-Security-Policy

Configuring Content-Security-Policy (CSP) and allowing Google Tag Manager (GTM) scripts can be split into two main parts:

  1. Setting GTMs standard tag types.
  2. Setting GTMs Custom HTML tag types.

The first part will be covered in short notes to provide a handy overview. However, the main concern of this article is the second part, as it is a bit more tricky to set.
Also, note that using unsafe-inline defeats the whole purpose of CSP, so that's not an option.

0) Nonce

The only practical approach for CSP-allowing is to use the unique server-generated nonce value, created either via an appropriate library or simply generating the proper random string. The same nonce value can be used for all scripts, but it must be uniquely generated for each client. For example, generating it in javascript could look like this:

const GENERATED_NONCE = crypto.randomBytes(16).toString("base64");
Enter fullscreen mode Exit fullscreen mode

Add the rule to CSP header and allow generated nonce:

<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'nonce-{GENERATED_NONCE}'" />
Enter fullscreen mode Exit fullscreen mode

1) Allowing GTM and it's standard tag types

This part is fairly simple and nicely documented in developers.google.com.

Outlined main steps are:

  1. Whitelist nonce in the CSP header (already done in the previous section of this article).
  2. Use nonce-aware version of GTM snippet - it will propagate the nonce to its scripts.
  3. Whitelist necessary resources in the CSP header for the tags used (just follow errors in the console).

In some sense, this should be the end of this article, but unfortunately, GTM doesn't propagate the nonce to any Custom HTML tags.

2) Setting Custom HTML tag types

Getting the nonce variable in GTM

In order to add the nonce attribute to the Custom HTML scripts, it must be first defined as a GTM variable:

  1. Add id="gtmScript" to the nonce-aware version of GTM snippet - this will be used to target the element and capture nonce.
<script id="gtmScript" nonce="{GENERATED_NONCE}">
  // GTM function
</script>
Enter fullscreen mode Exit fullscreen mode
  1. In GTM, create a new variable that will capture the nonce. Use DOM Element type, and select the ID of the GTM snippet (gtmScript in this guide).

Alt Text

Allowing custom HTML script

Now that the nonce variable is available in the GTM, add it to the Custom HTML script.

<script nonce="{{nonce}}">
  console.log("CSP-allowed script with nonce:", "{{nonce}}");
</script>
Enter fullscreen mode Exit fullscreen mode

If the tag is not firing, check the Support document.write. This can be a key step in Single Page Applications.
The GTM Custom HTML script is now nonce-allowed and fires as expected.
Of course, any assets used by this script will now need to be allowed in the CSP header.

Alt Text

Script within a script

Many tracking scripts are creating and firing additional script within themselves.
These will also be blocked as inline-scripts.
Find out where and how they are created, and add nonce to them as well.

Usually, the code looks similar to this:

var script = document.createElement("script");
script.type = "text/javascript";
script.async = true;
script.src = "https://tracking.js";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(script, s);
Enter fullscreen mode Exit fullscreen mode

Edit this part of the code and insert the nonce variable, in the same manner along with other attributes.

script.nonce = "{{nonce}}";
Enter fullscreen mode Exit fullscreen mode

Again, pay attention and whitelist any necessary assets that are now being blocked from this newly allowed script.

That's it - Custom HTML script is now fully CSP-allowed.

Top comments (7)

Collapse
 
ranyehushua profile image
ranyehushua

This was so helpful thank you! Googles own docs do not explain needing to add the variable in tag-manager console and adding to the scripts your self.

One problem in your solution is with Chrome. Chrome masks the nonce attribute value so tag manager is unable to grab it and store it as a variable. I solved this by adding a data-nonce attribute and injecting the nonce value there on that same gtmScript element. Works great!

Collapse
 
defdac profile image
Daniel Larsson

As @ranyehushua points out you have to inject the nonce hash in your own data attribute that doesn't have "nonce" in the name. "data-nonce" will not work as suggested.
Also. Your custom tag can only be evaluated before dom.ready. This means that if you for example use consent and delay tags after cookie_consent_update (in the case of cookiebot) the tag will be blocked by CSP.

Collapse
 
kiturutin profile image
kiturutin

Won't exposure of 'nonce' be possible vulnerability?

Collapse
 
defdac profile image
Daniel Larsson

Yes it is. We opted for not using any nonce-functionality at all in GTM and instead built a CSP-editor in our CSM where it is possible to add new rules on the fly.
This way the marketing department working in GTM can't go nuts adding new stuff indiscriminately, making it easy to keep track of new cookie-producing externally loaded scripts etc. The CSP-rules becomes less of a hurdle and more a nifty way of book keeping externally loaded scripts.

Collapse
 
boler profile image
boler

Hi, thanks for the article! However I wasn't able to propagate nonce value to my custom HTML tag. Apparently it doesn't work when GTM tries to read nonce attribute from the script tag. However, several articles suggested slightly different approach by using the data-nonce property on a script tag. This would imply modifying the GTM variable configuration to read from data-nonce instead of nonce property. I tried this and my custom HTML tag fired.

Any idea on how I could modify GTM config so that I don't have to use this patchy data-nonce property?

Collapse
 
kraxi profile image
Kraxi

The whole purpose of using nonces with the CSP is to generate a nonce PER RESPONSE not per client. What is the best approach to update the nonce variable on the GTM side in the proper configuration?

Collapse
 
matijamrkaic profile image
Matija Mrkaic

The approach in this article will always give a unique nonce, i.e. refreshing the page generates a new nonce.