它看起来像是 jQuery 的一个错误或怪癖,它附加内联脚本的方式最终会丢弃它们的所有属性,我看不出有明显的修复方法
为了测试它,我使用了以下 HTML:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Security-Policy" content="default-src http://localhost 'nonce-123456' ; child-src 'none'; object-src 'none'; script-src 'nonce-123456';">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.js" nonce="123456"></script> <!-- HTML nonce works -->
<script nonce="123456">
// This works
console.log('Inline nonce works');
// This will also work
var s = document.createElement('script');
s.setAttribute('nonce', '123456');
s.textContent = 'console.log("Dynamically generated inline tag works")';
document.head.appendChild(s);
// This won't work
var s2 = document.createElement('script');
s2.setAttribute('nonce', '123456');
s2.textContent = 'console.log("Dynamically generated inline tag appended via jQuery doesn\'t work")';
$(document.head).append(s2); // This will throw a CSP error
</script>
</head>
<body>
</body>
</html>
使用 jQuery 追加时,会经过以下过程(减少了一点):
- 创建一个文档片段并向其附加一个脚本标签
- 将
type="false/" 属性应用于脚本标签
- 删除
type 属性
- 如果存在
src 属性,它会通过 Ajax 检索脚本(没有进一步调查)
- 如果不是,它运行
DOMEval(node.textContent.replace(rcleanScript, ""), doc)
DomEval 看起来像这样(添加了 cmets):
doc = doc || document;
var script = doc.createElement( "script" );
script.textContent = code;
doc.head.appendChild( script ).parentNode.removeChild( script );
如您所见,在添加新元素之前,没有任何属性会延续到新元素,因此 CSP 失败。
解决方案是只使用原生 JavaScript 来追加元素,而不是 jQuery,或者可能等待错误修复/响应您的报告。我不确定他们的理由是什么,以这种方式排除内联脚本标签的属性可能是一种安全功能?
以下内容应该可以在没有 jQuery 的情况下实现您想要的 - 只需将 textContent 属性设置为您的 JavaScript 源代码。
var script = document.createElement('script');
script.setAttribute('nonce', '<%=nonce%>');
script.textContent = '// Code here';
document.head.appendChild(script);
所以本质上,该特定行引发错误的原因是附加标签实际上是一个新标签,具有相同的代码并且没有应用任何属性,并且由于它没有 nonce 它被 CSP 拒绝。
更新:我已经修补了 jQuery 来解决这个问题(3.1.2-pre 修补但通过了所有测试),如果你使用我的最后一个修复我建议更新到这个版本!
缩小:http://pastebin.com/gcLexN7z
未缩小:http://pastebin.com/AEvzir4H
分店在这里:https://github.com/Brian-Aykut/jquery/tree/3541-csp
问题链接:https://github.com/jquery/jquery/issues/3541
代码改动:
第 ~76 行将 DOMEval 函数替换为:
function DOMEval( code, doc, attr ) {
doc = doc || document;
attr = attr || {};
var script = doc.createElement( "script" );
for ( var key in attr ) {
if ( attr.hasOwnProperty( key ) ) {
script.setAttribute( key, attr[ key ] );
}
}
script.text = code;
doc.head.appendChild( script ).parentNode.removeChild( script );
}
将attr 添加到var ~line 5717 上的语句到
var fragment, first, scripts, hasScripts, node, doc, attr,
将第 5790 行附近的 else 正文更改为:
attr = {};
if ( node.hasAttribute && node.hasAttribute( "nonce" ) ) {
attr.nonce = node.getAttribute( "nonce" );
}
DOMEval( node.textContent.replace( rcleanScript, "" ), doc, attr );