内容安全策略是否仅在初始渲染期间强制执行?

信息安全 xss javascript 内容安全策略 jQuery
2021-08-19 10:48:07

CSP 是否仅在初始渲染期间强制执行,这意味着在您的文档加载后没有连续覆盖?这是我正在谈论的一个例子:

假设您的页面example.com有一些 JS 接受 url 参数name并将其呈现在页面上:

<script nonce="test123">
$(document).ready(function() {
    $("#id").html(nameParameterTakenFromURLWithoutSanitation);
});
</script>

如果有人访问example.com?name=<script>alert('XSS');</script>,根据我的测试,无论 CSP 如何,脚本仍将执行(弹出警报)。只有存在于初始服务器响应中的非法脚本才会被阻止。

我正在使用随机数的“严格”CSP 方法。这是我正在使用的政策:

object-src 'none';
script-src 'nonce-test123' 'unsafe-inline' 'unsafe-eval' 'strict-dynamic' https: http:;
report-uri http://localhost:8080/csp-collector

为什么脚本没有被阻止?

3个回答

介绍

只要您停留在页面上,浏览器就应该强制执行 CSP,而不仅仅是在最初加载或呈现页面时。其他一切都会使它变得没有牙齿。

因此,您遇到的行为与脚本加载无关。相反,它是关于加载它的内容。有两个问题。

问题1:严格动态

我会让Mozilla解释它:

strict-dynamic 指令指定显式地赋予标记中存在的脚本的信任,通过伴随它的随机数或散列,应传播到由该根脚本加载的所有脚本。

因此,当您通过给它一个随机数来信任一个脚本时,它可以反过来加载其他脚本。这意味着,如果您在带有 nonce 的脚本中存在 DOM XSS 漏洞,CSP 将无法拯救您。解决方案是删除'strict-dynamic'(但如果您依赖此功能,那当然会出现问题)。

问题 2:不安全评估

要加载脚本,请使用 jQuery .html()jQuery 源代码的某个深处(准确地说是第 343 行),它调用了旧的eval().

所以如果你运行代码

$(".test").html("<scr" + "ipt>alert('XSS');</scr" + "ipt>");

jQuery 会出于某种原因(不要问我为什么,我尝试过但未能遵循源代码)运行以下命令:

eval("alert('XSS');");

这意味着 CSP 不再适用 - 毕竟您不是在创建新的脚本标签,而是在评估一个字符串。

这里的解决方案是'unsafe-eval'从 CSP 中删除,但这会破坏 jQuery。解决方案可能是升级到 jQuery 3,在粗略检查源代码后似乎已经解决了这个问题。

结论

这意味着表单上的所有代码(使用 jQuery 2)

x.html(unsafeFromURL);

即使设置了 CSP,也可能容易受到 XSS 攻击我认为这对很多人来说是出乎意料的(对我来说)。但它提醒了另一个重要的一点:您不应该使用 CSP 作为对抗 XSS 的唯一防线

另请注意,这不是 CSP 工作方式的问题,而是 jQuery 工作方式的问题。

您的示例首先禁用 CSP 的内联脚本和评估保护,这是其主要的两个安全控制;特别是内联的,因为它是防止 XSS 的主要方法。

然后,您运行一个脚本,该脚本将<script>标签写入页面,从而将其放入页面中。如果您通过服务器端代码手动将相同的脚本标记写入页面,那么您将得到相同的结果。您使用 JavaScript 完成的这一事实在这里几乎无关紧要。

通过禁用 CSP 的关键保护功能,您删除了所有针对 XSS 的保护,然后获得了 XSS。

要回答您的问题的标题:不,除非您转到完全不同的 URL,否则 CSP 会在 HTML 文档/资源的整个生命周期内强制执行。

strict-dynamic由于在支持它的浏览器和不支持它的浏览器上存在您的脚本未被阻止的原因unsafe-inline将导致它未被阻止。

让我们看看支持的浏览器的案例strict-dynamic

strict-dynamic 的主要目的是摆脱白名单(由于 JSONP 等原因,它不那么安全。请参阅此处)并允许动态加载脚本,这是由网络上的许多 API 完成的。

基本上,如果您通过创建脚本标签来动态加载脚本,如果您使用的是随机数,它将被阻止。因此,strict-dynamic 所做的是,如果允许具有随机数并因此受信任的脚本可以动态加载这样的脚本(脚本标签创建)。

所以,在你的情况下:

<script nonce="test123">
    $(document).ready(function() {
        $("#id").html(nameParameterTakenFromURLWithoutSanitation);
    });
</script>

如果 nameParameter 是say,则创建一个脚本标记:<script>alert('XSS');</script>因此根据定义strict-dynamic它应该执行。

我主要关心的是你为什么要使用.html()您希望 name 有一些 HTML 标记吗?如果不是,则使用.text()which 转义任何 HTML 标记,从而防止 XSS。