nonce
给GTM以允许标签的使用。使用支持nonce的GTM代码片段可成功应用于除“自定义HTML”外的所有标签类型。
是否有一种方式可以将nonce
传递给“自定义HTML”,并允许使用自定义脚本,而不使用unsafe-inline
?nonce
给GTM以允许标签的使用。使用支持nonce的GTM代码片段可成功应用于除“自定义HTML”外的所有标签类型。
是否有一种方式可以将nonce
传递给“自定义HTML”,并允许使用自定义脚本,而不使用unsafe-inline
?nonce
属性添加到自定义HTML脚本中,必须首先将其定义为GTM变量:
id =“gtmScript”
添加到支持nonce
的GTM代码片段 - 这将用于定位元素并捕获nonce
。<script id="gtmScript" nonce="{GENERATED_NONCE}">
// GTM function
</script>
现在GTM中有nonce
变量,将其添加到自定义HTML脚本中。
<script nonce="{{nonce}}">
console.log("CSP-allowed script with nonce:", "{{nonce}}");
</script>
许多跟踪脚本在自身内部创建并触发其他脚本。这些脚本也将被视为内联脚本而被阻止。找出它们是如何创建的,以及在其中添加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指南的作者。
有一种更简单、更安全的方法来解决GTM未传播nonce到自定义HTML标签的问题。
<!-- 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 -->
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自定义脚本中的任何内容。我发现了与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变量中。
欢迎评论/建议,非常感谢。
对于那些遇到Chrome隐藏nonce属性问题的人(由Keyhan和Dan在https://dev59.com/m1EG5IYBdhLWcg3wfviM#65100705中提到),我发现Google Tag Manager有一个设置,可以从全局的“JavaScript变量”获取变量。
你只需要先设置那个全局变量。如果你动态添加Google Tag Manager,它可以在你的Google Tag Manager脚本之前设置。
window.nonceForCustomScripts = 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'));
// 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中列出的攻击。
document.write
来插入带有 nonce 的脚本? - nik10110script.setAttribute("nonce", "{{nonce}}");
而不是script.nonce = "{{nonce}}";
。 - chriskaya