阅读(2017) (0)

Flask JSON 安全

2021-08-10 10:38:38 更新

ECMAScript 5 的变更

从 ECMAScript 5 开始,常量的行为变化了。现在它们不由 Array 或其它 的构造函数构造,而是由 Array 的内建构造函数构造,关闭了这个特殊的 攻击媒介。

JSON 本身是一种高级序列化格式,所以它几乎没有什么可以导致安全问题,对吗? 你不能声明导致问题的递归结构,唯一可能导致破坏的就是在接受者角度上,非常 大的响应可以导致某种意义上的拒绝服务攻击。

然而有一个陷阱。由于浏览器在 CSRF 问题上工作的方式, JSON 也不能幸免。幸运 的是, JavaScript 规范中有一个怪异的部分可以用于简易地解决这一问题。 Flask 通过避免你做危险的事情上为你解决了一些。不幸的是,只有在 jsonify() 中有这样的保护,所以如果你用其它方法生成 JSON 仍然 有风险。

那么,问题是什么,并且怎样避免?问题是 JSON 中数组是一等公民。想象你在 一个 JSON 请求中发送下面的数据。比如 JavaScript 实现的用户界面的一部分, 导出你所有朋友的名字和邮件地址。并不罕见:

[
    {"username": "admin",
     "email": "admin@localhost"}
]

这当然只在你登入的时候,且只为你这么做。而且,它对一个特定 URL 上的所有 GET 请求都这么做。比如请求的 URL 是 http://example.com/api/get_friends.json

那么如果一个聪明的黑客把这个嵌入到他自己的网站上,并用社会工程学使得受害 者访问他的网站,会发生什么:

<script type=text/javascript>
var captured = [];
var oldArray = Array;
function Array() {
  var obj = this, id = 0, capture = function(value) {
    obj.__defineSetter__(id++, capture);
    if (value)
      captured.push(value);
  };
  capture();
}
</script>
<script type=text/javascript
  src=http://example.com/api/get_friends.json></script>
<script type=text/javascript>
Array = oldArray;
// now we have all the data in the captured array.
</script>

如果你懂得一些 JavaScript 的内部工作机制,你会知道给构造函数打补丁和为 setter 注册回调是可能的。一个攻击者可以利用这点(像上面一样上)来获取 所有你导出的 JSON 文件中的数据。如果在 script 标签中定义了内容类型是 text/javascript ,浏览器会完全忽略 application/json 的 mimetype ,而把其作为 JavaScript 来求值。因为顶层数组元素是允许的(虽然 没用)且我们在自己的构造函数中挂钩,在这个页面载入后, JSON 响应中的数据 会出现在 captured 数组中。

因为在 JavaScript 中对象字面量( {...} )处于顶层是一个语法错误,攻 击者可能不只是用 script 标签加载数据并请求一个外部的 URL 。所以, Flask 所做的只是在使用 jsonify() 时允许对象作为顶层元素。确保使用 普通的 JSON 生成函数时也这么做。