getJSON() 是不是可以安全地调用不受信任的 URL?
Posted
技术标签:
【中文标题】getJSON() 是不是可以安全地调用不受信任的 URL?【英文标题】:Is getJSON() safe to call on untrusted URL?getJSON() 是否可以安全地调用不受信任的 URL? 【发布时间】:2015-05-15 09:04:17 【问题描述】:使用来自不受信任的来源(例如另一个用户)的 URL 参数调用 jQuery 的 $.getJSON()
是否安全?换句话说,使用不受信任的 URL 调用 $.getJSON()
是否安全?我会小心不要信任响应并安全地处理响应,但是调用本身会带来安全风险吗?
换句话说,我说的是这样的:
$.getJSON(url_from_user, function(...) ... handle response safely ...);
或
$.getJSON('http://evil.com/foo.json', function(...) ...);
如果某些不受信任的用户为url_from_user
提供恶意值或有人恶意控制evil.com
站点,这是否允许代码注入或XSS?同样,假设返回的任何 JSON 对象都将得到安全处理。
我所做的更多细节和研究
documentation for getJSON 没有说明这种情况是否安全。从逻辑上讲,我希望这个场景是安全的,因为我希望 jQuery 的实现通过 XHR 下载 JSON 对象的文本,使用安全的 JSON 解析器解析这个文本,然后返回 JSON 对象。
不过,看了jQuery源码之后,我有些怀疑这是否安全。浏览the source code for jQuery,看起来这种情况可能允许 XSS。 getJSON() 的代码有点复杂(参见src/ajax.js),但它似乎选择了一个“传输”,然后用它来发送AJAX 请求。我看到src/ajax/script.js 注册了一个名为“脚本标签黑客传输”的传输。这个传输大致如下工作:它向文档添加一个脚本标签,例如<script src="http://evil.com/foo.json">
,并注册一个在下载脚本执行时运行的 onload 处理程序。换句话说,如果站点由攻击者控制,“脚本标签黑客传输”从根本上是不安全的:它将攻击者控制的脚本包含到文档中并执行它。除了脚本标签 hack 传输之外,还有一个使用浏览器 XMLHttpRequest() API 的 XHR 传输。我很难遵循决定在什么条件下使用“脚本标签黑客”传输的曲折逻辑。
那么,回到我最初的问题,使用用户提供的 URL 调用 $.getJSON()
是否安全?如果它在某些条件下可能不安全,那么在什么条件下(例如浏览器版本)它是安全/不安全的?
【问题讨论】:
如果用户自己指定该 URL,那么除了他们自己之外,还有谁可能会受到任何可能的 XSS 或任何伤害?或者您是否打算也为 其他 用户请求这些 URL,例如用户 A 输入 URL,而用户 B 在访问该站点时获得显示的结果……? @CBroe,是的,是后者。您可以将“用户提供”视为“来自不受信任的来源”的同义词。您列出的场景将是如何发生这种情况的一个示例。我已经编辑了我的问题,希望能更清楚地说明这一点。现在清楚了吗? 嗯,有两种情况——要么在 URL 下提供的数据是有效的 JSON 数据,然后它只取决于你如何处理这些数据;或者不是——在这种情况下,它根本不应该被处理。也许是第三种,其中 URL 包含某种代码,该代码会触发某些浏览器中导致漏洞的错误 - 但您可能会遇到用户提供的 any 类型的 URL,甚至一些简单的外部图像。也许请求该 URL 服务器端,检查数据的有效性,然后才通过您的服务器将其传递给客户端......? @CBroe,您是否阅读了我的问题中的倒数第二段?我知道这是很多文字,但我认为它直接解决了您的评论。您列出了两个或三个可能的场景,但这些并不是唯一可能的场景。另一种可能性是 jQuery 本身 做了一些引入 XSS 的事情,而不管我对数据做了什么。事实上,我在我的问题中详细解释了为什么看起来可能是这种情况——或者,至少,我不能排除这种可能性。 (出于与您详述的相同原因,让我们将可能的假设浏览器错误视为超出范围。) 好吧,插入脚本元素的方法通常用于规避 SOP——您可以嵌入任何来源的脚本,而 XMLHttpRequest 只能从同一来源加载数据,或者启用 CORS 的资源。如果您可以限制将用于满足这些条件的 URL,那么您可以使用 XMLHttpRequest 请求它们,而不是插入可能有害的脚本元素;如果不是,我认为通过您可以预先检查内容的服务器代理请求可能是唯一的“安全”选项。 【参考方案1】:除非您将请求配置为从不使用 JSONP(在某些情况下 jQuery 会自动尝试将其用于某些跨源请求),否则对任何随机的外部 URL 使用 $.getJSON()
是不安全的。
如果 jQuery 切换到 JSONP,这将直接启用脚本从其他来源注入您的页面,因为 JSONP 通过脚本注入精确工作(为了规避常规 Ajax 调用的同源限制)。
为了防止这种类型的误用,您必须阻止对 JSONP 的任何使用,并且必须研究在 jQuery 中执行此操作的最可靠方法。您也许可以切换到$.ajax()
,在那里您可以指定更多选项来控制事物。
如果这是我的代码,我什至可能会因为这个 Ajax 调用而完全跳过 jQuery,而只使用我自己的 xmlHttpRequest
对象来绝对保证它只是在执行纯 Ajax 调用(不回退到任何其他像 JSONP 一样传输)。
更新:
我一直试图找到$.getJSON()
将在 jsFiddle 上的各种测试场景中发出 JSONP 请求的情况。我一直找不到。目标站点有一个 Access-Control-Allow-Origin 标头允许跨源请求,在这种情况下,jQuery 只是执行跨源 Ajax 调用,或者它没有标头,jQuery 只是使 getJSON()
调用失败。因此,看起来需要对特定版本的 jQuery 进行认真研究才能确定它是否真的可以在您没有明确要求的情况下被欺骗以某种“自动”模式进行 JSONP 调用。
更新 2:发现一个实际漏洞
我发现了一个漏洞。如果发送到 $.getJSON()
的 URL 包含查询参数 callback=
,则 jQuery 将执行 JSONP,目标主机可以在响应中注入它想要的任何脚本。
这是一个使用可公开访问的 Flickr JSONP 端点的演示:
http://jsfiddle.net/jfriend00/z6ah9eh2/
这并没有恶意,但它确实通过$.getJSON()
执行目标站点的任意javascript。因此,它肯定容易受到 JSONP 代码注入的影响。
这是来自$.getJSON()
的 jQuery 文档的引用:
如果 URL 包含字符串
"callback=?"
(或类似的,定义为 服务器端 API),请求被视为 JSONP。
并且,“被视为 JSONP”意味着它将插入一个脚本标签并从 URL 中的站点请求和运行脚本 - 如果您使用 JSONP 访问不受信任的站点,则会打开一个跨站点脚本漏洞。
JSON 背后的一个想法是,它可以使用纯文本解析器进行解析,该解析器严格遵守 JSON 规范,并且除了纯 JSON 之外,没有任何东西是允许或可以通过的。如果有人试图将一些 Javascript 代码偷偷带入 JSON 字符串,任何半体面的 JSON 解析器都会将 JSON 视为无效而拒绝并抛出异常。在适当的世界中($.getJSON()
是),JSON 不使用 Javascript 解析器解析,而是使用它自己的文本解析器进行解析,该解析器严格只接受有效的 JSON,而不是其他 Javascript 构造。
这就是 JSON 解析器的安全实现背后的理念,据信$.getJSON()
使用了该解析器(任何解析器中总是可能存在未知错误,但已将其设计为安全的)。
所以,这个障碍已经过去了。没有任何技巧可以插入到一段 JSON 中,该段 JSON 由一个像样的 JSON 解析器解析,会导致后门代码注入。
现在,另一个障碍取决于您对 JSON 本身所做的事情以及您对 JSON 的处理或使用是否会导致潜在的不良行为。
例如,如果您从 JSON 中提取字符串属性并将其作为对象上的方法执行,而不检查该字符串是否为预期值,那么您的代码可能会被欺骗执行方法你不是故意的。这仍然不会将代码插入您的页面,但它确实执行了您不打算执行的操作。您可以通过在使用数据之前对数据进行适当的验证来避免这种情况。因此,如果如您所说,您正在安全地使用 JSON,那么这应该不是问题。
【讨论】:
@D.W. - 添加了实际 JSONP 漏洞的演示。诀窍是将callback=
添加到URL 查询参数,然后$.getJSON()
将使用脚本标记注入来尝试JSONP 调用。这允许目标站点直接将它想要的任何 JS 注入响应中。
非常有帮助。感谢您对漏洞的详细分析和具体演示。我想这是我需要记住避免的一个新的漏洞向量。太糟糕了,jQuery 文档中没有记录风险。再次感谢!
@D.W. - $.getJSON()
的 jQuery 文档确实这样说:JSONP - 如果 URL 包含字符串“callback=?” (或类似的,由服务器端 API 定义),请求被视为 JSONP。有关更多详细信息,请参阅 $.ajax() 中对 jsonp 数据类型的讨论。 幸运的是,大多数人并没有在用户指定的 URL 上使用$.getJSON()
(潜在的恶意外部代理可以访问这些 URL)指定整个 URL),因此这对其他人来说通常不是问题。通常使用的 URL 是由开发人员编码的。您的情况与平时不同。以上是关于getJSON() 是不是可以安全地调用不受信任的 URL?的主要内容,如果未能解决你的问题,请参考以下文章
charles进行手机抓包:安全证书不受信任,错误码3 怎么解决下?