JQuery 3.1.1违反了CSP指令

Vic*_*par 9 jquery content-security-policy

我正在使用JQuery 3.1.1版,我正在尝试在我的网页上实现内容安全策略指令.

我收到以下错误:

拒绝执行内联脚本,因为它违反了以下内容安全策略指令:"script-src'self''nonce-c20t41c7-73c6-4bf9-fde8-24a7b35t5f71'".要么是'unsafe-inline'关键字,哈希('sha256-KAcpKskREkEQf5B3mhDTonpPg34XnzaUC5IoBrOUrwY ='),要么是nonce('nonce -...')来启用内联执行.

该错误发生在主jquery.js脚本文件的第82行.这一行的内容是:

doc.head.appendChild( script ).parentNode.removeChild( script );
Run Code Online (Sandbox Code Playgroud)

基本上,它会向DOM添加内联脚本标记,这违反了CSP.

我不想用'unsafe-inline'.有没有其他方法来规避这个错误?

正如您在CSP违规中看到的那样,我正在使用CSP级别2(nonce),但它会被忽略.在附加脚本标记时,是否有可能(某些方式)通知jQuery使用此nonce?


更新:这是HTML的样子(使用Express模板作为nonce)

<script nonce="<%=nonce%>" type="text/javascript" src="jquery.js"></script>
Run Code Online (Sandbox Code Playgroud)

呈现HTML后,脚本标记上的nonce属性与服务器发送的CSP nonce指令匹配.


更新:它确实适用于普通的JavaScript

<script nonce="<%=nonce%>" type="text/javascript">
        var userEmail = "<%=user.user.email%>";
</script>
Run Code Online (Sandbox Code Playgroud)

如果没有nonce属性,此脚本标记将违反CSP指令.

Bri*_*ian 2

它看起来像是 jQuery 的一个 bug 或怪癖,它附加内联脚本最终会丢弃它们的所有属性,而且我看不到修复它的明显方法来测试它,我使用了以下 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>
Run Code Online (Sandbox Code Playgroud)

当使用 jQuery 追加时,它会经历以下过程(稍微减少):

  • 创建文档片段并向其附加脚本标签
  • type="false/"属性应用于脚本标签
  • 删除type属性
  • 如果一个src存在属性,它将通过 Ajax 检索脚本(没有进一步研究这一点)
  • 如果没有则运行DOMEval(node.textContent.replace(rcleanScript, ""), doc)

DomEval看起来像这样(添加了注释):

doc = doc || document;
var script = doc.createElement( "script" );
script.textContent = code;
doc.head.appendChild( script ).parentNode.removeChild( script );
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,在附加新元素之前,没有任何属性会继承到新元素,因此 CSP 会失败。

解决方案是仅使用本机 JavaScript 来附加元素,而不是 jQuery,或者可能等待错误修复/对报告的响应。我不确定他们的理由是什么,以这种方式排除内联脚本标签的属性,这可能是一个安全功能?

以下内容应该可以在没有 jQuery 的情况下实现您想要的效果 - 只需将 textContent 属性设置为您的 JavaScript 源代码即可。

var script = document.createElement('script');
script.setAttribute('nonce', '<%=nonce%>');
script.textContent = '// Code here';
document.head.appendChild(script);
Run Code Online (Sandbox Code Playgroud)

因此,该特定行抛出错误的本质原因是附加标签实际上是一个具有相同代码的新标签,并且没有应用任何属性,并且由于没有属性,因此nonce被 CSP 拒绝。

更新:我已经修补了 jQuery 来解决这个问题(3.1.2 之前修补过,但通过了所有测试),如果您使用我的上一个修复,我建议更新到此版本!

缩小版: 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 );
}
Run Code Online (Sandbox Code Playgroud)

添加attrvar第 5717 行的语句

var fragment, first, scripts, hasScripts, node, doc, attr,
Run Code Online (Sandbox Code Playgroud)

将第 5790 行附近的正文更改else为:

attr = {};
if ( node.hasAttribute && node.hasAttribute( "nonce" ) ) {
    attr.nonce = node.getAttribute( "nonce" );
}
DOMEval( node.textContent.replace( rcleanScript, "" ), doc, attr );
Run Code Online (Sandbox Code Playgroud)