node.js 沙盒逃逸分析

更新日期: 2020-10-30 阅读: 2.1k 标签: node

作者: 凹凸曼 - nobo


背景

日常开发需求中有时候为了追求灵活性或降低开发难度,会在业务代码里直接使用 eval/Function/vm 等功能,其中 eval/Function 算是动态执行 JS,但无法屏蔽当前执行环境的上下文,但 node.js 里提供了 vm 模块,相当于一个虚拟机,可以让你在执行代码时候隔离当前的执行环境,避免被恶意代码攻击。


vm 基本介绍

vm 模块可在 V8 虚拟机上下文中编译和运行代码,虚拟机上下文可自行配置,利用该特性做到沙盒的效果。例如:

const vm = require("vm");

const x = 1;
const y = 2;

const context = { x: 2, console };
vm.createContext(context); // 上下文隔离化对象。

const code = "console.log(x); console.log(y)";

vm.runInContext(code, context);
// 输出 2
// Uncaught ReferenceError: y is not defin

根据以上示例,可以看出和 eval/Function 最大的区别就是可自定义上下文,也就可以控制被执行代码的访问资源。例如以上示例,除了语言的语法、内置对象等,无法访问到超出上下文外的任何信息,所以示例中出现了错误提示: y 未定义。以下是 vm 的的执行示例图:


沙盒环境代码只能读取 VM 上下文 数据


沙盒逃逸

node.js 在 vm 的文档页上有如下描述:

vm 模块不是安全的机制。 不要使用它来运行不受信任的代码。

刚开始看到这句话的很好奇,为什么会这样?按照刚才的理解他应该是安全的?搜索后我们找到一段逃逸示例:

const vm = require("vm");

const ctx = {};

vm.runInNewContext(
    'this.constructor.constructor("return process")().exit()',
    ctx
);
console.log("Never gets executed.");

以上示例中 this 指向 ctx 并通过原型链的方式拿到沙盒外的 Funtion,完成逃逸,并执行逃逸后的 JS 代码。

以上示例大致拆分:

tmp = ctx.constructor; // Object

exec = tmp.constructor; // Function

exec("return Process");

以上是通过原型链方式完成逃逸,如果将上下文对象的原型链设置为 null 呢?

const ctx = Object.create(null);

这时沙盒在通过 ctx.constructor,就会出错,也就无法完成沙盒逃逸,完整示例如下:

const vm = require("vm");

const ctx = Object.create(null);

vm.runInNewContext(
    'this.constructor.constructor("return process")().exit()',
    ctx
);
// throw Error

但,真的这样简单吗?

再来看看以下成功逃逸示例:

const vm = require("vm");
const ctx = Object.create(null);

ctx.data = {};

vm.runInNewContext(
    'this.data.constructor.constructor("return process")().exit()',
    ctx
);
// 逃逸成功!
console.log("Never gets executed.");

为什么会这样?

原因

由于 JS 里所有对象的原型链都会指向 Object.prototype,且 Object.prototype 和 Function 之间是相互指向的,所有对象通过原型链都能拿到 Function,最终完成沙盒逃逸并执行代码。

逃逸后代码可以执行如下代码拿到 require,从而并加载其他模块功能,示例:

const vm = require("vm");

const ctx = {
    console,
};

vm.runInNewContext(
    `
    var exec = this.constructor.constructor;
    var require = exec('return process.mainModule.constructor._load')();
    console.log(require('fs'));
`,
    ctx
);

沙盒执行上下文是隔离的,但可通过原型链的方式获取到沙盒外的 Function,从而完成逃逸,拿到全局数据,示例图如下:



总结

由于语言的特性,在沙盒环境下通过原型链的方式能获取全局的 Function,并通过它来执行代码。

最终确实如官方所说,在使用 vm 的时应确保所运行的代码是可信任的。

eval/Function/vm 等可动态执行代码的功能在 JavaScript 里一定是用来执行可信任代码。

以下可能是比较常见会用到动态执行脚本的场景:模板引擎,H5 游戏、追求高度灵活配置的场景。


解决方案

  • 事前处理,如:代码安全扫描、语法限制
  • 使用 vm2 模块,它的本质就是通过代理的方式来进行安全校验,虽然也可能还存在未出现的逃逸方式,所以在使用时也谨慎对待。
  • 自己实现解释器,并在解释器层接管所有对象创建及属性访问。

欢迎关注凹凸实验室博客:aotu.io

或者关注凹凸实验室公众号(AOTULabs),不定时推送文章


本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!

链接: https://fly63.com/article/detial/9784

相关推荐

怎么卸载nodejs?

Node.js是一个Javascript运行环境,可以使Javascript这类脚本语言编写出来的代码运行速度获得极大提升,那么安装后该如何卸载呢?下面本篇文章就来给大家介绍一下Windows平台下卸载node.js的方法,希望对大家有所帮助。

happypack提升项目构建速度

运行在 Node.js 之上的 Webpack 是单线程模型的,也就是说 Webpack 需要处理的任务需要一件件挨着做,不能多个事情一起做。happypack把任务分解给多个子进程去并发的执行,子进程处理完后再把结果发送给主进程。

nodejs 异步转同步

nodej项目在微信环境开发,nodejs的异步特效,会导致请求没有完成就执行下面的代码,出现错误。经过多方查找,可以使用async模块来异步转同步,只有前一个function执行callback,下一个才会执行。

node.js反向代理的实现

在实际工程开发中,会有前后端分离的需求。使用node.js反向代理的目的:实现前后端分离,前端减少路径请求的所需的路由文件;通过http-proxy-middleware中间件、Http Proxy 模块这2种方式实现node.js的反向代理

Ubuntu 上 Node.js 安装和卸载

Ubuntu 安装 Node.Js:执行检查可更新的软件,先用普通的apt工具安装低版本的node,然后再升级最新。更换淘宝的镜像,这个是必须的,用过的node的人都知道。安装更新版本的工具N

nodejs 文本逐行读写功能的实现

利用nodejs实现:逐行读写(从一个文件逐行复制到另外一个文件);逐行读取、处理和写入(读取一行,处理后,写入另一个文件)1.所需要的模块: fs,os,readline。功能的实现:readWriteFileByLine.js,功能的调用:index.js

使用pkg打包Node.js应用的方法步骤

Node.js应用不需要经过编译过程,可以直接把源代码拷贝到部署机上执行,确实比C++、Java这类编译型应用部署方便。然而,Node.js应用执行需要有运行环境,意味着你需要先在部署机器上安装Node.js

query和params在前后端中的区别

最近在学node,试着做一个前后端都有的项目,然后就遇到了query和parmas这俩兄弟,你说他们俩长得也不像吧,可这用法实在是太类似了,专门写篇文章来区分这哥俩,分别会从vue路由和Node接收两个角度讲

用node.js开发一个可交互的命令行应用

在这个教程中,我们会开发一个命令行应用,它可以接收一个 CSV 格式的用户信息文件,教程的内容大纲:“Hello,World”,处理命令行参数,运行时的用户输入,异步网络会话,美化控制台的输出,封装成 shell 命令,JavaScript 之外

Node.js 应用:Koa2 使用 JWT 进行鉴权

在前后端分离的开发中,通过 Restful API 进行数据交互时,如果没有对 API 进行保护,那么别人就可以很容易地获取并调用这些 API 进行操作。那么服务器端要如何进行鉴权呢?

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!