javascript

Интеграция Google Tag Manager (GTM) с Content Security Policy

  • понедельник, 31 марта 2025 г. в 00:00:10
https://habr.com/ru/articles/895696/

Примечание: Пост предназначен для веб-аналитиков и специалистов, работающих с GTM, поэтому не несет в себе подробных инструкций по имплементации на стороне разработки.

Соответствие политикам Content Security Policy (CSP) при использовании Google Tag Manager (GTM) на сайте может быть сопряжено с рядом сложностей.

CSP блокирует загрузку внешних ресурсов, выполнение inline-скриптов и стилизации, что может вызвать сбои в работе GTM: некорректную загрузку тегов, проблемы с передачей данных или отсутствие панели отладки.

Ниже рассмотрены основные проблемы в работе GTM и возможные способы их решения. Некоторые из них решаются совместно с отделом разработки и безопасности, а некоторые решаются на стороне самого GTM.

Блокировка inline-скриптов и стилей

GTM использует inline-скрипты и inline-стили, но CSP может их блокировать. Это может повлиять как на работу тегов, так и на корректное отображение самого интерфейса режима превью GTM.

Ошибки в отображении Preview GTM
Ошибки в отображении Preview GTM

Важно отметить, что проблема с блокировкой стилей затрагивает не только визуальное отображение интерфейса превью GTM. Многие рекламные пиксели вставляют на страницу <noscript>-блоки или скрипты, которые используют inline-стили. При жёсткой CSP такие элементы также могут быть заблокированы, что повлияет на корректную работу пикселя и сбор данных.

Решение:

Необходимо обновить политику CSP на стороне разработки, разрешив выполнение inline-кода с использованием безопасных механизмов (учитывая особенности GTM Preview, в котором не все методы применимы).

1. Использование nonce в сочетании со strict-dynamic

Рекомендуется добавить nonce к доверенному загрузчику скриптов (например, gtm.js или тегу, вставляющему его). В сочетании с директивой strict-dynamic это позволяет:

  • Разрешить выполнение как основного скрипта, так и всех дочерних скриптов, загружаемых по цепочке

  • Косвенно разрешить выполнение inline-скриптов GTM Preview, если они добавляются доверенными скриптами

Директива strict-dynamic включает наследование разрешений всем скриптам по цепочке. Это означает, что если исходный доверенный скрипт (например, a.js) динамически загружает b.js, а тот, в свою очередь, — c.js, то при наличии strict-dynamic выполнение будет разрешено для всех трёх. В отсутствие этой директивы будет выполнен только изначально доверенный скрипт (a.js), а последующие — заблокированы политикой безопасности.

2. Для inline-стилей:

Добавить style-src 'unsafe-inline' только если нет иной возможности, либо аналогичный механизм с nonce для <style>.

Ограничения на загрузку внешних ресурсов

CSP может ограничивать загрузку внешних скриптов, изображений и iframe, используемых в GTM. Это приводит к тому, что аналитические теги не срабатывают, рекламные пиксели не отправляют события, а данные о действиях пользователя теряются.

Пример ошибки, которая может появиться в консоли браузера при попытке отправки аналитических данных из GTM:

Пример ошибки про попытке отправки данных из GTM
Пример ошибки

Такие ошибки означают, что браузер заблокировал отправку данных, потому что политика CSP запрещает подключение к этому ресурсу.

Решение:
Команда разработки должна обновить политику CSP, добавив разрешения (в connect-src, img-src, frame-src и других директивах) на загрузку ресурсов со ВСЕХ доменов GTM и всех подключённых сервисов.

Ошибка с eval() и ее причины

Ошибка, связанная с eval(), возникает, когда GTM пытается выполнить строку как JavaScript-код.

Пример функции eval():

eval("console.log('Hello, world!')");

Этот код выполнит строку как JavaScript, выводя "Hello, world!" в консоль. Однако, из-за своей опасности (возможность выполнения непроверенного кода), многие браузеры и политики безопасности (включая CSP) ограничивают или блокируют использование eval().

Пример ошибки eval() в консоли
Пример ошибки eval() в консоли

Данная ошибка со стороны gtm.js возникает при использовании Custom JavaScript переменных, которые используют eval() для динамического выполнения кода. Однако из-за ограничений CSP, использование eval() может быть заблокировано, что приведёт к сбоям в работе GTM.

Как решить проблему с eval() в GTM

Для решения этой проблемы есть два пути:

1) Использование Custom HTML тега в GTM:

При необходимости парсинга данных из структуры DOM рекомендуется использовать Custom HTML теги в GTM. Это позволяет избежать использования eval() в кастомных переменных.

Рекомендации:

  • Минимизировать обращения к DOM через JavaScript
    Например, при загрузке страницы сохраните в отдельную переменную весь document, и получайте данные уже из полученной переменной, вместо многократных обращений ко всему DOM.

  • После сбора данных отправлять их в dataLayer и использовать тип переменной dataLayer Variable вместо Custom JavaScript.

2) Шаблон для вычислений:

Для расчёта некоторых переменных с более сложной логикой можно создать отдельный Custom Template и вынести в него все функции вычислений. Такой подход позволяет централизовать обработку данных внутри GTM и не нарушать политику CSP.

Пример шаблона:

const copyFromDataLayer = require('copyFromDataLayer');

const variableName = data.variable;

const variables = {
 'variable1': function() { return 'value1'; },
 'variable2': function() { return 'value2'; }
 };

if (Object.keys(variables).indexOf(variableName) > -1) {
 return variablesvariableName;
 } else {
 return undefined;
 }

Доступ к функциям, localStorage и cookies внутри Custom Template управляется через раздел Permissions, что позволяет ограничить работу с потенциально небезопасными источниками и предотвратить ошибки, связанные с использованием eval().

В дальнейшем вместо Custom JavaScript переменных рекомендуется использовать переменную на основе созданного шаблона, передавая в неё имя необходимой функции. Такой подход снижает количество кастомного кода в конфигурации GTM и повышает уровень безопасности.

Пример использования кастомного шаблона
Пример использования кастомного шаблона

Хеширование внешних скриптов

В некоторых случаях имеет смысл использовать хеши внешних скриптов, загружаемых через GTM — особенно если речь идёт о малознакомых партнёрах, CPA-сетках или временных интеграциях. Хеширование позволяет браузеру убедиться, что содержимое скрипта не изменилось, и тем самым снизить потенциальные риски.

Процесс хеширования обычно выглядит так:

  • Получение хеша содержимого скрипта

  • Добавление хеша в CSP, чтобы браузер мог безопасно разрешить их выполнение.

  • Отслеживание актуальности: при изменении скрипта или добавлении новых партнёров хеши нужно обновлять.

Этот подход не всегда удобен, особенно для крупных партнёров, где скрипты обновляются автоматически и хеш быстро устаревает. В таких случаях возможно использование whitelisting доверенных доменов в script-src, однако при наличии strict-dynamic такие списки по умолчанию игнорируются. Существуют способы обойти это ограничение, включая конфигурации с двумя заголовками Content-Security-Policy, но подобные решения нестабильны, зависят от поведения конкретного браузера и требуют осторожности при внедрении.