为了实现内容安全策略,我需要将
nonce
传递给 GTM 以允许标签。
使用nonce-aware版本的GTM代码片段适用于除自定义HTML之外的所有标签类型。
有没有办法将
nonce
传递给 自定义 HTML 并允许自定义脚本,而不使用 unsafe-inline
?
为了将
nonce
属性添加到自定义 HTML 脚本中,必须首先将其定义为 GTM 变量:
id="gtmScript"
添加到 GTM 片段的随机数感知版本 - 这将用于定位元素并捕获 nonce
。<script id="gtmScript" nonce="{GENERATED_NONCE}">
// GTM function
</script>
现在
nonce
变量在 GTM 中可用,请将其添加到自定义 HTML 脚本中。
<script nonce="{{nonce}}">
console.log("CSP-allowed script with nonce:", "{{nonce}}");
</script>
如果标签未触发,请检查支持 document.write。这可能是单页应用程序中的关键一步。 GTM 自定义 HTML 脚本现在已被允许并按预期触发。 当然,现在需要在 CSP 标头中允许此脚本使用的任何资产。
许多跟踪脚本都在自身内部创建和触发其他脚本。 这些也将作为内联脚本被阻止。 找出它们是在哪里以及如何创建的,并向它们添加
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.toguide
的作者我在 Matija Mrkaic 提出的原始解决方案中发现了与其他人相同的问题。他的文章非常有用,但我发现
nonce
数据属性返回空白值。
为了解决这个问题,我在 GTM 脚本中添加了一个
data-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 中公开随机数值,这种“临时”解决方案背后的理论是仅在短时间内公开它,直到加载到 Google 跟踪代码管理器变量中为止。
欢迎评论/建议,非常感谢。
对于任何遇到 Chrome 隐藏随机数属性问题的人(Keyhan 和 Dan 在 https://stackoverflow.com/a/65100705/3370010 中指出),我发现 Google 跟踪代码管理器有一个设置可以从中获取变量全局“JavaScript 变量”。
您只需要先设置该全局变量即可。如果您动态添加 Google 跟踪代码管理器,则可以在 Google 跟踪代码管理器脚本之前进行设置。
window.nonceForCustomScripts = nonce;
如果您只是将代码插入 Google 标签管理器脚本中,它看起来会像这样
(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');
这比添加 data-nonce 属性更安全,因为它可以防止基于 CSS 的攻击,例如 https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/nonce 中列出的攻击
有一种更简单、更安全的方法来解决 GTM 不将随机数传播到自定义 HTML 标签的问题。
<!-- Google Tag Manager -->
<script nonce='{SERVER-GENERATED-NONCE}'>
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 自定义脚本中的任何内容。