Using Google Analytics And Google Tag Manager With Content Security Policy

July 20, 2017
Using Google Analytics and Google Tag Manager with Content Security Policy

Content Security Policies are extremely helpful when configured properly, but may need to be updated to properly allow Google Analytics and Google Tag Manager to function as expected. If your website is already using Content Security Policy, this blog post will explain how to modify your policy to allow Google Analytics and Google Tag Manager. Creating a new policy from scratch is outside the scope of this blog post. If you aren’t already using Content Security Policy, you don’t need to start using it to use Google Analytics or Google Tag Manager.

Content Security Policy (CSP) is a web standard that allows websites to restrict third-party assets from using certain features that might cause security concerns. This is mostly a good thing, because it prevents The Bad Guys from running malicious JavaScript and stealing your users’ passwords. However, CSP can’t inherently tell the difference between code written by The Bad Guys and non-malicious code delivered from a perfectly innocuous third-party tool like Google Analytics or Google Tag Manager. If you want your website to make use of both CSP and Google tooling, you will need to configure your CSP to trust Google as one of The Good Guys.

How Content Security Policy Works

The Content Security Policy for a webpage is sent in the “Content-Security-Policy” header of the HTTP response that contains the web page. This means that it is not part of the HTML of the web page, and cannot be accessed by viewing the page’s source code. (It is possible to deliver CSP inside the HTML through meta tags, but this is not recommended.) You will need to use the Network tab of your browser’s Developer Tools to view the Content Security Policy for a given web page.

The content of this HTTP header is called the policy for that webpage. The policy consists of a sequence of directives, separated by semicolons. Each directive consists of a directive name followed by a source list. The directive name indicates what type of resource is being restricted, such as JavaScript files or iframes. The source list is an allowlist of locations that will be trusted to provide the resource type indicated by the directive name.

For example, a policy might look like the following:

img-src self cdn.example.com; script-src self google.com

This policy has two directives. The first directive restricts where image resources can be loaded from, and allowlists the site’s own domain and an external content delivery network. The second directive restricts where JavaScript files can be loaded from, and allowlists the site’s own domain and Google. With this policy in place, a browser will refuse to load images from any domains not on the first list (including Google!), and will refuse to load JavaScript from any domains not on the second list (including the image CDN!). Because the policy contains no directives for frames or styles, the browser will still load iframes, CSS, and fonts from any external domain.

There is a special directive named default-src which defines a default source list for a handful of other directives, including script-src, img-src, style-src, and connect-src. If any of the other directives are implemented they add additional restrictions the source list provided by default-src, but if default-src is defined it will restrict all of the different resource types affected by the Content Security Policy.

How Not to Break Your Entire Website

This part is very important.

Because Content Security Policy causes the browser to refuse to load certain resources, it is quite possible for a poorly-written policy to break your website in new and exciting ways. Follow these two rules to avoid breaking your entire website and making everybody angry.

Do not add a second policy. Instead, add domains to the existing policy.

Do not add new directive names that are not already present in your policy. If one of the directives mentioned below is not already being used, simply don’t add it.

Violating either of these rules will create new restrictions on what resources can be loaded. Our goal is to update the policy to allow more resources, not fewer. Any resources that were previously allowed to load were probably being used, and restricting them from loading will probably break something.

Content Security Policy for Google Analytics

Google Analytics may make use of up to four features restricted by Content Security Policy, although it can be configured to use as few as two.

JavaScript

The script-src directive restricts who may add JavaScript code to the page. Google Analytics requires JavaScript to run, so the script-src directive must be updated to allow it. There are actually two sources of JavaScript code for Google Analytics. The first is simply https://www.google-analytics.com, which should be added to the source list of your script-src directive (or your default-src directive). Note that the www is mandatory, while the https is optional.

The second source of JavaScript is the Google Analytics code snippet that you add to your page (the one that starts with (function(i,s,o,g,r,a,m)) is an inline snippet, which is also restricted by Content Security Policy. Inline snippets are a major problem for cross-site scripting attacks, but they are also incredibly useful for development, so CSP provides several ways of addressing this issue.

The easiest, but least secure, way to allow Google Analytics to run is to add the special string ‘unsafe-inline’ (with quotes) to the source list for your script-src directive. As its name implies, this allows all inline snippets to run, and may allow unsafe code to run. The second-easiest method is to move the Google Analytics code snippet to an external code file, hosted on a domain that is already allowlisted by your script-src directed, such as the primary domain of your website. If you are using Google Tag Manager, you have already done this! The third way is to use a nonce-value on the inline script. The use of nonce-values is complicated, and only recommended if you are already using nonce-values for other inline scripts.

Tracking Beacons

Google Analytics has three ways to send data to Google’s servers: Image requests, Post requests, and the browser “Beacon” feature. All three of these methods can be restricted by Content Security Policy; Image requests by the img-src directive, and the other two by the connect-src directive (both of these directives are affected by the default-src directive). By default, Google Analytics uses the image tag for small requests and Post requests for large requests. This can be overridden by explicitly setting the ‘transport’ variable in order to control what resource type is used, although the Beacon method is not available in all browsers.

If directives are in place that cover any of the beacon types used on your website, either explicitly or via the default-src directive, then Google Analytics must be allowlisted by adding https://www.google-analytics.com to the source list for those directives. If you have enabled Advertising Features or AdWords integration, you should also add https://stats.g.doubleclick.net and https://www.google.com to the source list.

Examples

The following policy is the simplest one that would allow Google Analytics to function without Advertising or AdWords features, and without allowing any other non-Google resources.

default-src 'self' https://www.google-analytics.com 'unsafe-inline'

The following policy only allows what is strictly necessary. This policy also requires that the Google Analytics code snippet be moved to a separate file hosted on the same domain as your main website.

script-src 'self' https://www.google-analytics.com;
img-src https://www.google-analytics.com www.google-analytics.com https://stats.g.doubleclick.net;
connect-src https://www.google-analytics.com www.google-analytics.com https://stats.g.doubleclick.net

Google Tag Manager

The role of Google Tag Manager is to load other assets onto your page, including asset types that might by restricted by Content Security Policy. Tag Manager itself only requires enable two features of Content Security policy, but assets loaded by Tag Manager may require other policies.

JavaScript

Like Google Analytics, Google Tag Manager requires enabling two sources of JavaScript. Unlike Google Analytics, there is no workaround for allowing ‘unsafe-inline’ script execution. Tag Manager is fundamentally a script-injection framework. Its entire purpose is to dynamically load and execute pieces of JavaScript onto your page. It is not feasible to restrict Tag Manager from executing inline snippets. In order for Tag Manager to function, then your script-src (or default-src) directive must be updated to include https://www.googletagmanager.com and ‘unsafe-inline’ (with the quotes) in the source list.

In order to use Custom JavaScript variables in your Tag Manager container, you will also need to add the ‘unsafe-eval’ directive (with quotes) to your source list. This directive restricts the use of constructs that execute plain strings as JavaScript code, such as the eval() function, and the less-well-known new function() construct. GTM uses eval() to execute code provided by the user of GTM as JavaScript code on the page, after doing its own check for validity.

Like its similarly-named cousin ‘unsafe-inline’, the use of ‘unsafe-eval’ removes some of the protection that CPS is intended to provide. The use of this source should be weighed against utility of Custom JavaScript variables before it is deployed to production. Custom JavaScript variables are currently the only asset type in GTM that use functionality that requires additional allowlisting. Other similar asset types, such as Custom HTML tags and Zones, do not use eval() and do not require special treatment in your policy. Note that if CSP is blocking GTM’s use of eval() on your site, you will not see it reported in your console–GTM swallows this warning the same way that it does other errors that are thrown from a Custom JavaScript tag, and the value of your Custom JavaScript variable will be undefined.

Hat-tip to Simo Ahava for identifying the issues with Custom JavaScript variables in the comments below. Find more Content Security Policy tips in his Google Tag Manager Best Practices series!

Other Assets

Content Security Policy must also be configured to allow any assets contained inside of Tag Manager. For example, if you are using Tag Manager to deploy Google Analytics, you will need to follow all of the instructions in the section above for modifying your policy to allow Google Analytics. If you are also loading additional third-party tracking pixels, you will need to figure out how to permit the appropriate image or script sources. The best way to figure out what you need to do is to try adding the tracking code in Tag Manager, preview it, and then view the error messages in your browser’s console. Browsers generally have helpful error messages about Content Security Policy violations, except for ‘unsafe-eval’ as described above.

Debug Mode

The Debug Pane in Google Tag Manager requires an entirely different set of permissions than the rest of Tag Manager. It loads data from a different domain, and must also load style information because it is responsible for displaying visual information to the user. Because the Debug Pane is not used for general website viewing, you may want your policy to always allow these additional resources. You could use a more permissive policy on your dev servers, or see the following section about using Charles Proxy to edit the policy for a page while you are viewing it.

In order for the Debug Pane to be usable, the domain tagmanager.google.com must be added to the source list for the script-src directive if it is defined explicitly or through the default-src directive. Additionally, if the style-src directive is defined, either explicitly or through the default-src directive, then its source list must be updated to include both tagmanager.google.com and ‘unsafe-inline’ (with quotes). Content Security Policy may block additional requests for icon images and fonts, but allowing these are not necessary for the Debug Pane to be functional.

Using Charles to Modify Content Security Policy

In a previous post, I explained how to set up Charles Proxy to debug Google Analytics requests. Charles can also be used to test and debug edits to the Content Security Policy for your site, by editing the HTTP headers of requests before they are received by your browser.

From the Tools menu, select “Rewrite.” Underneath the left list, click “Add” to create a new set of Rewrite rules. In this set of rules, add a new Location at the top and enter your site’s address. Add a new rule at the bottom that will overwrite the Content Security Policy header.

Inside the rule, select “Modify Header” from the dropdown, and make sure the checkbox for Response is selected and Request is cleared. Under the “Replace” menu, enter “Content-Security-Policy” for the name and your new policy as the vale. Viola! You are in business.

Summary

Content Security Policy is a useful browser feature for enhancing the security of your website. Using it in conjunction with Google Analytics requires some coordination, but the amount of effort is minimal. In order to use Google Tag Manager, you will lose some of the benefits of CSP and you will have to do more work to manage what assets must be allowed, but it still provides significant security benefit with a manageable amount of maintenance overhead.