GTM未将随机数传递给自定义HTML标签

13
为了实现内容安全策略,我需要传递nonce给GTM以允许标签的使用。使用支持nonce的GTM代码片段可成功应用于除“自定义HTML”外的所有标签类型。 是否有一种方式可以将nonce传递给“自定义HTML”,并允许使用自定义脚本,而不使用unsafe-inline
4个回答

13
为了将nonce属性添加到自定义HTML脚本中,必须首先将其定义为GTM变量:
  1. id =“gtmScript”添加到支持nonce的GTM代码片段 - 这将用于定位元素并捕获nonce
<script id="gtmScript" nonce="{GENERATED_NONCE}">
  // GTM function
</script>

在GTM中,创建一个新的变量以捕获nonce。使用DOM元素类型,并选择GTM片段的ID。

GTM Variable configuration for nonce


现在GTM中有nonce变量,将其添加到自定义HTML脚本中。

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

如果标签没有触发,请检查{{Support document.write}}。这是单页面应用程序中的关键步骤。 现在,GTM自定义HTML脚本已被允许使用nonce并按预期触发。 当然,此脚本使用的任何资产现在都需要在CSP头中允许。

GTM Custom HTML configuration


嵌套脚本

许多跟踪脚本在自身内部创建并触发其他脚本。这些脚本也将被视为内联脚本而被阻止。找出它们是如何创建的,以及在其中添加nonce

通常,代码看起来类似于以下内容:

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);

编辑代码的这一部分,并以与其他属性相同的方式插入{{nonce}}变量。

script.nonce = "{{nonce}}";

再次注意,要将任何被新允许的脚本所阻止的必要资产列入白名单。

就这样 - 自定义HTML脚本现在完全允许使用CSP。


来源和免责声明:我是expanded dev.to指南的作者。


1
你好,我不确定如何实现“脚本中的脚本”部分。您建议下载第三方脚本并进行修改吗?然后在 GTM 自定义 HTML 中使用 <script> 修改后的代码 </script>,而不是使用 <script src="third-party-link"></script>? - ETNyx
1
感谢您的回复,经过数小时的头痛,我终于解决了我的问题。这个“第三方链接”使用document.write,所以GTM中的这个小小的复选框做了一些魔术,解决了我的问题,nonce就足够了,正如您所说的。再次感谢。 - ETNyx
1
@Aaryn,你是否勾选了“支持document.write”复选框?另外,如果不允许使用“unsafe-inline”,控制台中有任何错误吗? - Matija Mrkaic
有没有想过为什么即使脚本只是将某些内容记录到控制台,也需要使用 document.write 来插入带有 nonce 的脚本? - nik10110
3
为了让它在Chrome上工作,我必须使用script.setAttribute("nonce", "{{nonce}}");而不是script.nonce = "{{nonce}}"; - chriskaya
显示剩余9条评论

0

有一种更简单、更安全的方法来解决GTM未传播nonce到自定义HTML标签的问题。

  1. 确保您已在GTM脚本标签中包含了nonce属性,如官方文档中所述:
<!-- Google Tag Manager -->
<script nonce='{SERVER-GENERATED-NONCE}'>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;var n=d.querySelector('[nonce]');
n&&j.setAttribute('nonce',n.nonce||n.getAttribute('nonce'));f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-XXXXXX');</script>
<!-- End Google Tag Manager -->

接下来,在内容安全策略(CSP)头部的script-src部分添加'strict-dynamic',如下所示。'strict-dynamic'简化了基于nonce或哈希的CSP实施过程,因为它自动允许执行之前受信任脚本生成的脚本。这个操作还方便了使用各种第三方JavaScript库和小部件,包括GTM。
Content-Security-Policy:
  script-src 'nonce-{SERVER-GENERATED-NONCE}' 'strict-dynamic';
  img-src www.googletagmanager.com;
  object-src 'none';
  base-uri 'none';

此外,阻止object-src是一个明智的做法,以防止使用可能有危险的插件,如Flash,而base-uri则可以防止攻击者更改从相对URL加载的脚本的位置。
通过这种解决方案,无需添加不安全的data-nonce属性,也无需更改GTM自定义脚本中的任何内容。

-1

我发现了与Matija Mrkaic提出的原始解决方案相同的问题。他的文章非常有用,但我发现nonce数据属性返回的是空值。

为了解决这个问题,我在GTM脚本中添加了一个data-nonce属性,并在标签管理器中使用变量来提取nonce值(类似于Matija Mrkaic)。这个新变量被称为data-nonce。然后,我在GTM中添加了一个自定义HTML标签,在加载完成后删除data-nonce属性。

GTM代码:

<script id="gtmScript" nonce='{{csp_nonce}}' data-nonce='{{csp_nonce}}'>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;var n=d.querySelector('[nonce]');
n&&j.setAttribute('nonce',n.nonce||n.getAttribute('nonce'));f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','{{GOOGLE_TAG_MANAGER_ID}}');</script>

用于删除一次性值的GTM自定义HTML标签,一旦它被加载。

<script nonce="{{data-nonce}}">
    console.log("Inline script to remove data-nonce.");
    document.getElementById("gtmScript").removeAttribute("data-nonce");
</script>

这个解决方案远非完美,但我目前还没有找到一种方法来传递“秘密”给GTM。在DOM中公开nonce值并不推荐,这个“临时”的解决方案的理论是只在短时间内公开它,直到加载到Google Tag Manager变量中。

欢迎评论/建议,非常感谢。


1
它可能看起来像nonce是空的,因为它在浏览器预览中被隐藏了,但是可以通过JavaScript访问:https://dev59.com/EMDqa4cB1Zd3GeqPbFMs - waternova

-1

对于那些遇到Chrome隐藏nonce属性问题的人(由Keyhan和Dan在https://dev59.com/m1EG5IYBdhLWcg3wfviM#65100705中提到),我发现Google Tag Manager有一个设置,可以从全局的“JavaScript变量”获取变量。

Google Tag Manager Configuration for JavaScript Variable

你只需要先设置那个全局变量。如果你动态添加Google Tag Manager,它可以在你的Google Tag Manager脚本之前设置。

window.nonceForCustomScripts = nonce;

如果您只是将代码插入到Google Tag Manager脚本中,它看起来会像这样。
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
            new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
            j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
            'https://www.googletagmanager.com/gtm.js?id='+i+dl;var n=d.querySelector('[nonce]');
            n&&j.setAttribute('nonce',n.nonce||n.getAttribute('nonce'));
// Added code
w.nonceForCustomScripts = n.nonce||n.getAttribute('nonce');
// End added code
            f.parentNode.insertBefore(j,f);
            })(window,document,'script','dataLayer','your-gtm-id');

这比添加数据随机属性更安全,因为它可以防止基于CSS的攻击,例如https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLElement/nonce中列出的攻击。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接