怎么可能毒化 JavaScript 数组构造函数,ECMAScript 5 如何防止这种情况发生?

信息安全 javascript
2021-08-13 16:53:00

从他们的 Django 文档中JsonResponse

ECMAScript 第 5 版之前,可能会毒化 JavaScript Array 构造函数。因此,默认情况下,Django 不允许将非字典对象传递给 JsonResponse 构造函数。然而,大多数现代浏览器都实现了 EcmaScript 5,它消除了这种攻击媒介。因此,可以禁用此安全预防措施。

以 JavaScript 数组的形式从服务器向浏览器发送数据到底有多不安全?这种攻击怎么称呼?

我的实验表明,虽然默认情况下 Django 会抱怨向浏览器发送这样的内容:["foo", "bar"],但它不会抱怨发送这样的内容:{"foo": ["bar", "baz"]}发送包裹在对象中的数组比发送“原始”数组更安全吗? ?

EMCAScript 5 如何防止这种情况发生?

1个回答

与污染 Array 构造函数相关的攻击称为JSON 劫持这是一个称为跨站点脚本包含(XSSI) 的问题的一个实例,它源于浏览器通常允许网站包含跨域的外部 JS 文件这一事实。

假设您网站上经过身份验证的用户可以在此 API 位置访问一些“秘密令牌”:

https://yoursite.example/api/secret_tokens

假设 API 响应如下所示:

tokens = ["secret123", "secrect456"]

这会造成漏洞。evil.example攻击者可以通过向登录用户发送包含如下代码的链接来轻松窃取登录用户的令牌:

<script src="https://yoursite.example/api/secret_tokens"></script>
<script>
    alert(tokens)
</script>

这样,受害者的浏览器就会被欺骗,将 API 响应解释为外部 JS 文件。API 无意中创建了有效的 JS 代码(将数组分配给全局变量tokens),从而将登录用户的令牌泄漏到evil.example.

现在,让我们考虑一下您的 API 响应看起来与此类似的更有可能的情况:

["secret123", "secret456"]

这仍然是有效的 JS 代码,但由于它只是一个没有赋值的表达式,攻击者无法像上面那样轻松访问数组的内容。这里的解决方法是简单地覆盖 Array 构造函数:

Array = function() { foo = this; }

这个特殊的技巧在 2007 年在 Firefox 中被报告并修复,但从那时起,其他技术,例如使用Object.__defineSetter__or Object.defineProperty,已经被发现(并修复)。

EMCAScript 5 如何防止这种情况发生?

ES5 规范要求始终使用[]内置Array构造函数(同样适用于对象构造函数):

15.4.1.1 数组 ( [ item1 [ , item2 [ , ... ] ] ] )

创建并返回一个新的 Array 对象,就像在具有相同参数Array的表达式中使用标准内置构造函数 (15.4.2)。new

最后,让我们考虑 API 使用 JSON 对象回复的情况:

{"tokens": ["secret123", "secret456"]}

从未受到攻击的简单原因是它是无效的 JS:花括号中的孤立对象被解析为块而不是对象因此,您不能将此 JSON 对象作为外部脚本包含在内而不触发错误。从历史上看,触发语法错误一直是防止 XSSI 攻击的便捷方法。

以下是对 JSON 劫持的一些额外参考: