SARS 2019 第四周打卡(SecureJSON)
本周是开学周,整个工作日都在开会,学习时间并不多。周四晚上参加字节跳动的宣讲会,简单的介绍了目前字节跳动的技术栈。其中字节跳动的 http 框架使用的是 Gin,因此周六周日就捣鼓起来了,其中就遇到了一个奇怪的功能——SecureJSON。
根据文档描述 SecureJSON 可以防止 json 劫持,如果给定的结构是数组值,则默认预置 "while(1);"
到响应体。
1 | func securejson() { |
运行示例代码,响应体被插入了"while(1);"
。
1 | λ curl http://127.0.0.1:8080/securejson |
根据文档的描述将 Array 修改成 Map。
1 | func securejson() { |
此时响应体前并未添加"while(1);"
。
1 | λ curl http://127.0.0.1:8080/securejson1 |
难道[]
是造成的所谓 JSON 劫持的原因吗,JSON 劫持到底是什么,通过查阅资料找到了一段示例代码。
某银行可以通过某个 Get 方法的 API 获取已登录用户的账户余额。
1 | GET https://mybank.com/users/balance |
正常情况下 Cookie 和同源策略可以阻止 JS 跨域获取数据,那攻击者该如何获取到数据呢。在浏览器控制台实验发现以[]
开头的 Javascript 代码可以直接执行。
1 | eval('[1,2,3]'); |
也就是说如果将[]
格式的数据放入<script>
标签下可以直接被执行,即便如此又该如何获取到数据呢?这时候 Javascript 覆盖和重写特性就被利用了-如果有多个重名的函数与方法只有最后一个定义的有效。攻击者通过重写默认的Array()
函数,并将返回[]
格式的 API 放入<script>
标签下,一旦运行到[
就会调用覆写的Array()
来窃取数据。
1 | <script>function Array(){ |
主流浏览器通过删除__defineSetter__
阻止覆写修复了该漏洞。但随着 ES6 Proxy 的发布,Array()
又可以被覆写利用了。尽管浏览器很快修复了这些漏洞,Google、Facebook 等厂商通过在自己的 API 前添死循环阻止执行到[
,也是为了避免今后出现其它利用方法。客户端则需要先将附加的安全头去除再进行处理。
这也是为什么 Gin 的 SecureJSON 不对{}
格式的 JSON 进行处理,JS 会首先检查语法,导致{}
格式的 API 无法被执行。
1 | eval('{"key":"value"}'); |
虽然只是一个小问题,但深入研究探究其中的缘由还是很有趣的。