JSON无法解析的废物:为什么这么严重?

26

阅读了 this 这个问题,问为什么谷歌/脸书等会在他们的JSON响应中添加无法解析的冗余代码,例如:

  • while(1);
  • for(;;);
  • &&&START&&& ... &&&END&&&
  • 1和3组合

我已经理解了动机。但是我仍然不清楚为什么要使用这些相对复杂的机制,当可以通过以下方式实现类似的效果:

  • 在开头添加额外的)使整行无效并出现语法错误
  • 用注释包装JSON
现在,看起来这种添加了无限循环和(奇怪的)语法错误保护的方法是为了绕过旧的、宽容的javascript解析器,但我似乎找不到任何参考资料证明这一点。有另一个SO问题甚至对while(1);的解决方法进行了批评(指出1可能会被覆盖),并拒绝了另一种形式的解决方法{}&&,但没有解释原因或引用任何来源。
其他参考资料:

这是否只是我一个人感觉这是一种通过模糊来保障安全,并且对于任何合理编写的XSS攻击都不构成问题?JSON.parse()实际上会调用eval()吗? - Gung Foo
一个相关的问题是为什么它们不直接返回JSON对象而是数组。返回对象会产生完全有效的JSON,但不是有效的Javascript,以更清晰的方式防止JSON劫持。可能的答案是这些服务试图返回尽可能紧凑的格式以节省带宽。大多数Web服务并不在如此大规模的操作中,可以避免这种级别的优化。 - Ben Regenspan
2个回答

10

我认为有几个与无法解析的垃圾形式相关的细节:

  • {}&& 前缀的起源可以追溯到 JSON 解析器(例如较旧版本的 Dojo)未将 JSON 字符串验证为有效的 JSON 语法。现在我知道的所有 JSON 解析库都进行验证,但是这篇来自2008年的博客文章表明,所述版本的 dojo 允许正常地 JSON.parse json,而 eval 将失败,这会方便地保护针对 JSON 的劫持攻击。

  • while(1) 可以使用 Number 原型赋值 01 来失效。

  • for(;;)while(1) 都会导致网站崩溃,这在某种程度上增加了保护,因为任何进一步执行任何脚本的操作都将被完全停止而没有错误。这很重要,因为按照定义,javascript 中的错误不标记脚本执行的结尾,而 for(;;) 确保在其之后没有任何脚本被执行。这是为了防止可能情况下攻击者成功地通过利用 window.onerror 的弱点、重写 eval 或代理错误对象实例化(如重写 Error.prototypeconstructor)来拦截脚本错误。

    更新

    同时还有这个关于安全性的问题建议不要使用for(;;)或者while(1),因为这样会让人误以为你的网站正在攻击客户端的CPU或触发恶意软件扫描器。我认为现代浏览器运行在沙盒环境和按Tab分隔的基础上,不存在严重的DoS问题。但对于旧的浏览器来说确实是个问题。而恶意软件扫描器则是一个真正的问题,可能会将你的网站报告为攻击源。

  • &&&START&&&(以及相应的&&&END&&&标签)比仅使用)或可能会无意中关闭的注释更容易解析接收JSON数据的客户端,并且能够提高程序员的可读性和可见性。使用注释只是一种变化,因为它提供了/*开始和*/结束标记。在我看来,清晰明显的起始和结束标记有助于注意到这些无用信息的含义。使用注释并不能真正提供这种效果。


3
关于“1 can be clobbered”:
如果您在 Webkit 中执行以下操作:
var test = 1;
console.log(test.constructor == window.Number); //true is logged

理论上可能存在一种可能性,即有一种方法可以修改window.Number或其原型,使得1的值不是1

window.Number.prototype.toString = function() { return 0 };
window.Number.prototype.valueOf = function() { return 0 }; 

幸运的是这个方法并不起作用。但我认为这可能是作者想要表达的。

编辑 通常我也倾向于使用将内容包装在注释中的方法(但必须确保您的 JSON 对象不包含类似 {"test":"*/"} 这样的内容,因为这会导致语法错误。即使可以抛出异常,可能也会成为一个问题,因为它是否可被捕获以及可能会暴露出关于错误发生行的一些信息。或者如果 Error 对象本身可以修改,那也可能会有问题)。


实际上,您关于语法错误泄露信息的观点可能是仅仅在JSON前缀中添加无效字符串是一个坏主意的原因。这就排除了我提出的第一个选项(在JSON之前使用简单的前缀),但那么&&&START&&& ... &&&END&&&是做什么的? - Manav
@Manav,我猜想使用for(;;)while(1)&&&START&&&结合使用的一个原因是,首先尝试通过语法错误停止脚本的执行。如果这样做不起作用,至少可以通过无限循环阻止恶意网站。但说实话,我还需要再进行一些进一步的阅读。但我认为任何可以解析为代码或引发异常的内容都可能是有害的。 - t.niese
@Manav 另外一个注意事项是(但这只是猜测):由于当前的 JS 引擎在执行代码之前会进行大量的预处理和优化,因此不清楚在不知道引擎代码内部情况的情况下,代码中的对象何时以及如何初始化。这就是为什么我认为将 JSON 结果包装成注释是更好的解决方案。 - t.niese

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接