【问题标题】:jQuery 3.1.1 violation of CSP directivejQuery 3.1.1 违反 CSP 指令
【发布时间】:2017-07-15 23:48:29
【问题描述】:

我正在使用 jQuery 版本 3.1.1,我正在尝试在我的网页上实现 Content Security Policy (CSP) 指令。

我收到以下错误:

拒绝执行内联脚本,因为它违反了以下内容 内容安全策略指令:“script-src 'self' 'nonce-c20t41c7-73c6-4bf9-fde8-24a7b35t5f71'”。 'unsafe-inline' 关键字,一个哈希 ('sha256-KAcpKskREkEQf5B3mhDTonpPg34XnzaUC5IoBrOUrwY='),或随机数 ('nonce-...') 是启用内联执行所必需的。

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

doc.head.appendChild( script ).parentNode.removeChild( script );

基本上,它在 DOM 中添加了一个内联脚本标签,这违反了 CSP。

我不想使用'unsafe-inline'。是否有其他方法可以规避此错误?

正如您在 CSP 违规中看到的那样,我使用的是 CSP 级别 2(随机数),但它被忽略了。是否有可能(以某种方式)通知 jQuery 在附加脚本标记时使用这个随机数?


这就是 HTML 的样子(使用 Express.js 模板作为 nonce)

<script nonce="<%=nonce%>" type="text/javascript" src="jquery.js"></script>

一旦 HTML 被渲染,脚本标签上的 nonce 属性与服务器发送的 CSP nonce 指令相匹配。


它确实适用于纯 JavaScript:

<script nonce="<%=nonce%>" type="text/javascript">
        var userEmail = "<%=user.user.email%>";
</script>

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

【问题讨论】:

  • 它只适用于 javascript 还是直接将脚本放入 html 中?
  • 在将其附加到头部之前,您是否在与 CSP 的 nonce(减去“nonce-”前缀)匹配的脚本标签上设置了 nonce 属性?如果您已经这样做了,可能会看到脚本元素的外观和 CSP 标头可能会有所帮助。
  • @bormat 是的,它适用于纯 javascript。
  • @Brian 是的,我在脚本标签上获得了与 CSP 随机数匹配的随机数属性。
  • 刚刚更新了问题以反映 cmets

标签: jquery content-security-policy


【解决方案1】:

好的,您只想包含 jQuery,因此您需要在 jQuery 脚本之前和删除您的自定义函数之后重新定义函数 appendChild(以禁用它)。

<script>
    var oldAppend = document.head.appendChild
    document.head.appendChild = function(script){
        if(script && script.tagName=='SCRIPT'){
            document.createElement('fakeHead').appendChild(script)//because script need a parent in the line that create the error
            return script
        }
        return oldAppend.apply(this,arguments)
    }
</script>

<script nonce="<%=nonce%>" type="text/javascript" src="jquery.js"></script>

<script>
    document.head.appendChild = oldAppend
</script>

试试这个。它将重新定义jQuery的append函数:

var oldAppend = $.fn.append
$.fn.append = function($el){
    var dom = ($el instanceOf $) ? $el[0] : $el
    if(dom && dom.tagName=='SCRIPT'){
        this[0].appendChild(dom)
        return this
    }
    return oldAppend.apply(this,arguments)
}

【讨论】:

  • 感谢您的帮助,但不幸的是,您的解决方法并没有解决 JQuery 在执行 DOMEval 函数时剥离脚本属性的问题。
  • 我已经编辑过了,实际上jquery只保留src和作为属性并删除所有其他
【解决方案2】:

它看起来像是 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 );

【讨论】:

  • 对此非常奇怪的一点是,尽管该元素没有附加nonce 属性。在我的开发工具中和附加子项之前/之后,在 Chome 开发工具的元素窗格中没有属性可以查看,它神奇地存在并且可以通过s2.getAttribute('nonce') 访问。从外观上看,我可能错过了附加的文档片段,但没有执行特定的脚本标签,只有在 DOMEval 中附加的创建的标签,这可能解释了为什么它在元素中看起来都很好,但 CSP 不好:)
  • 我不认为.innerHTMLscript 元素上按预期工作。 stackoverflow.com/a/13392818/367865
  • 你是对的@Ouroborus,谢谢我已经修改,所以它使用textContent - .innerHTML 可以意外地逃避事情 - 我把两者弄糊涂了!谢谢:)
  • 嗨@Brian,感谢您的分析和重现问题的步骤。但是,您提出的解决方案并不能解决问题。 JQuery 为我的子页面中的每个脚本标记执行 DOMEval,删除 nonce 属性并生成 CSP 违规。
  • @VictorGaspar 如果您以任何可能剥离属性的方式使用 jQuery 评估脚本标记,则完全有可能 - DOMEval 被许多其他 jQuery 内部函数间接调用。我已经实现了一个 hack,它可能会有所帮助,但由于许多原因它并不理想,我认为更安全的解决方案是避免使用 jQuery 创建脚本元素并在你触摸内联脚本时几乎通过外观附加它们(选择器来创建标签或附加等)它将删除随机数。您目前如何处理脚本(创建 + 追加)?
猜你喜欢
  • 2020-01-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-23
  • 2021-03-14
  • 1970-01-01
  • 2021-08-26
  • 1970-01-01
相关资源
最近更新 更多