【AICon】探索八个行业创新案例,教你在教育、金融、医疗、法律等领域实践大模型技术! >>> 了解详情
写点什么

漫谈 B 端的沙箱技术

  • 2010-06-02
  • 本文字数:3207 字

    阅读完需:约 11 分钟

从语言学的角度上来说,允许代码无节制地使用全局变量,是最错误的选择之一。而更可怕的,就是一个变量"可能"成为全局的(在未知的时间与地点)。但是这两项,却伴随 JavaScript 这门语言成功地走到了现在。

也许是限于浏览器应用的规模,所以这一切还迟迟没有酿成灾难。在此之前,出现了两种解决方案。一种是 ECMA 在新的规范(Edition 5)中对此做出了限制,其中最重要的一条便是 eval() 的使用变得不再随意和无度。而另一种方案,则是相对没有那么官僚与学术的,尽管也拥有一个同样学术的名字:沙箱。

沙箱(Sandbox)并不是一个新东西,即使对于 JavaScript 来说,也已经存在了相当长的时间。在 SpiderMonkey JS 的源代码中,就明确地将一个闭包描述为一个沙箱。这包含着许多潜在的信息:它有一个初始环境,可以被重置,可以被复制,以及最重要的,在它内部的所有操作,不会影响到外部

当然事实上远非如此。JavaScript 里的闭包只是一个"貌似沙箱"的东西–仍然是出于 JavaScript 早期的语言规范的问题,闭包不得不允许那些"合法泄漏"给外部的东西。而对于这一切无法忍受的前端工程师们,开始寻求另外的解决之道,这其中相对较早的尝试,是基于 IFRAME 的实践。例如 dean.edwards 在 2006 年提出过的方案(注 1):

复制代码
a_frames.document.write(
"<script>"+
"var MSIE/*@cc_on =1@*/;"+ // sniff
"parent.sandbox=MSIE?this:{eval:function(s){return eval(s)}}"+
"<\/script>"
);

显然,由于在不同的 IFRAME 中运行着各自的 JavaScript 引擎实例,所以上述的方案也意味着沙箱是"引擎"这个级别的:在任何一个沙箱中的崩溃,将导致该引擎以及对应 IFRAME 崩溃。但–理论上说–不会影响整个浏览器。

问题是,这并不那么理想。往往的,引擎会导致整个浏览器锁在那里,例如用 alert() 弹出一个对话框而又因为某种意外失去了焦点。又或者单个的 IFRAME 会导致全局的 CPU 被耗光,例如一个死循环。于是更加复杂的方案–在 JavaScritp 中包含一个完整的执行器–出现了。最有名的则是 Narrative JavaScript,它内建了一个执行器,用于逐行地解释执行 JavaScript 代码,这使得它可以控制所有的代码执行序列,或者随时重置整个执行引擎–如同一个沙箱所要做的那样。

这一切或者太过依赖于环境,又或者太过复杂,但都不乏追随者。例如 jsFiddle 这个项目(注 2)在"嵌入或装载"这样的路子上就已经有了不俗的成绩。但是,YUI 在新版本中却给出了它自己的选择:以更加明确的编程约定,来实现应用级别的沙箱。这包括一个非常简单的、新的 YUI 语法:

复制代码
YUI().use('dom-base', function(Y) {
// Y 是一个新的沙箱
});

在’dom-base’位置上,可以是 1 到 N 个字符串,表明一个需要在沙箱中装载的模块列表。这可以是沙箱的初始列表,或者后续的 callback 函数 (亦即是用户代码) 所需依赖的模块列表。在这种实现方案中,YUI 为每个沙箱维护各自的装载模块列表和上下文环境中的变量、成员。但是出于 JavaScript 语言自己的局限,这个沙箱依然是相当脆弱的。例如下一示例中沙箱内的代码就会污染到全局:

复制代码
YUI().use('', function(Y) {
abc = 1234; //<-- 这里可能导致一个全局变量'abc'被隐式地声明
});

同样,在上述的沙箱里也可以使用类似 window、document 等全局变量、修改它们的成员或无限制地调用方法(例如使用 setTimeout() 来创建时钟)。所以 YUI 的沙箱事实上是靠"规约"来约束的,而不是真正意义上的沙箱。当然,这也意味着,如果用户能按照规约来处理沙箱内的代码,那么也就能自由地享用它带来的便利:安全、移植和有效的隔离副作用。

而我们再穷究其根底,YUI 沙箱的实质不过是一行:

复制代码
// code from yui.js
// - mod.fn(this, name)
mod.entryFunc(sandbox, modName);

其实际含义是:

  • mod :沙箱当前装载的模块;
  • entryFunc : 上述模块的入口函数;
  • sandbox :当前的沙箱的实例,即 YUI() 返回值;
  • modName:模块名

除了依赖关系(以及可能需要的异步加载)之外,YUI 沙箱环境仅是用下面的代码来简单地调用 callback 函数:

复制代码
callback(Y, response);

然而这些需求的实现并不那么复杂。首先,我们设定数据结构 mod 为一个对象:

复制代码
{ name:modName, fn: entryFunc, req: [], use: [] }

则一个环境对象 env,将包括多个 mod(将它们处理成对象而非数组,主要是便于使用名字来索引模块) 和以及对它们进行管理操作的方法:

复制代码
{ mods:{}, used:{}, add:..., use:...}

最后,所谓一个沙箱 sandbox,就是上述环境对象的一个实例,并在初始时 sandbox.mods 与 sandbox.used 为空。由此简单的实现为:

复制代码
/**
* tiny sandbox framework
* mirror from YUI3 by aimingoo.
**/
function Sandbox() {
if (!(this instanceof arguments.callee)) return new arguments.callee();
this.mods = this.mods || {};
this.used = {};
}
Sandbox.prototype = {
add: function(modName, entryFunc, reqArr, useArr) {
this.mods[modName] = { fn: entryFunc, req: reqArr, use: useArr }
},
use: function() {
var mods = [].slice.call(arguments, 0); // 0..length-2 is modNames
var callback = mods.pop(); // length-1 is callback
var recursive_load = function(name, mod) {
if (!this.used[name] && (mod=this.mods[name])) {
mod.req.forEach(recursive_load, this);
mod.fn(this, name);
mod.use.forEach(recursive_load, this);
this.used[name] = true;
}
}
mods.forEach(recursive_load, this);
callback(this);
}
}

现在我们来尝试一个与 YUI 类似的语法风格:

复制代码
Sandbox().use('', function(){
alert('user code.');
});

或者,先向整个 Sandbox 环境注册一些模块(在真实的框架实现中,这一步可能是通过框架的装载器来初始化):

复制代码
// for test, entry of mods
f1 = function() { alert('f1') };
f2 = function() { alert('f2') };
f3 = function() { alert('f3') };
// mods for global/common env.
Sandbox.prototype.mods = {
'core': { fn: f1, req: [], use: [] },
'oo': { fn: f2, req: ['core'], use: ['xml'] },
'xml': { fn: f3, req: [], use: [] }
}

然后再尝试在一个沙箱实例中运行代码:

复制代码
// f1 -> f2 -> f3 -> user code
Sandbox().use('oo', function(){
alert('user code.');
});

其实即便是上述代码中用于处理模块依赖的逻辑,也并不是什么"神奇的"代码或者技巧。除开这些,这样的沙箱隔离泄露的能力还抵不过一个嵌入式 DSL 语言。而后者所应用的技巧很简单,看不出什么花招(注 3):

复制代码
with (YUI()) this.eval("... mod_context ... ");

这样一来,在 mod_context 里的代码就只会在 YUI() 的一个实例中造成污染了。当然,仍然是源于 JavaScript 的限制,我们还是无法避免一个变量泄露到全局–除非,我们回到 js in js 这个项目(注 4),真的在环境中重新初始化一个 js 引擎。

从这一意义上来说,引擎级别的沙箱与操作系统的进程一样,带来的是终级的解决方案,所以 Chrome、IE 等等主流浏览器纷纷有了"独立进程"模式。而在这样的背景之下,试图用"框架内置沙箱"来改善 ECMAScript ed3 中一些设计疏失的种种努力,不过是一张张空头的支票罢了。

甚至,用这本支票签完单也未必有人会收的。

备注

注 1: http://dean.edwards.name/weblog/2006/11/sandbox/
注 2: http://jsfiddle.net/
注 3: http://blog.csdn.net/aimingoo/archive/2009/09/08/4532496.aspx
注 4: http://mxr.mozilla.org/mozilla/source/js/narcissus/


作者简介:周爱民,国内软件开发界资深软件工程师,架构师。有十余年的软件开发、项目管理、团队建设的经验,历任部门经理、区域总经理、高级软件工程师、平台架构师等职。现任支付宝(中国)公司业务架构师。

给 InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家加入到 InfoQ 中文站用户讨论组中与我们的编辑和其他读者朋友交流。

2010-06-02 00:056828

评论

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

Linux环境,C/C++语言手写代码实现线程池

Linux服务器开发

c++ 线程池 Linux后台开发 服务端开发 线程池源码

T3 出行 Apache Kyuubi Flink SQL Engine 设计和相关实践

网易数帆

sql 大数据

Linux之tr命令

入门小站

高层次人才一站式服务平台系统开发 探索全方位服务新模式

a13823115807

深度剖析「圈组」消息系统设计 | 「圈组」技术系列文章

网易云信

开发 社交软件

乘数科技加入PolarDB开源数据库社区

阿里云数据库开源

数据库 阿里云 开源 开源数据库 polarDB

与开源社区用户共创,首本《OceanBase 社区版入门到实战》电子书新鲜出炉啦!

OceanBase 数据库

6元自助洗车店加盟需要多少费用

共享电单车厂家

自助洗车加盟 6元自助洗车店加盟 6元自助洗车 自助洗车加盟费

系列解读SMC-R:透明无感提升云上 TCP 应用网络性能(一)| 龙蜥技术

OpenAnolis小助手

网络协议 技术分享 龙蜥社区 RDMA SMC-R

Dcm4chee--MySql版Docker镜像制作

birdbro

Docker DCM4CHE

自助扫码洗车机加盟怎么加

共享电单车厂家

自助洗车机价格 自助扫码洗车机 自助洗车怎么加盟 共享洗车加盟

江苏财政的数字新径,大型政企的云上坐标

脑极体

6元自助洗车设备一套多少钱一台

共享电单车厂家

自助洗车机多少钱 自助洗车机价格 自助洗车加盟 6元自助洗车设备 6元自助洗车机

脚本库详细说明 - 大屏云极简使用手册

shulinwu

体验了一把最近很火的开源项目-MASA Blazor

MASA技术团队

C# .net 微软 组件库

万亿级超高清产业变奏,分布式存储支撑关键应用落地

焱融科技

云计算 分布式 高性能 文件存储 影视渲染

在线HTML转MarkDown工具

入门小站

工具

6元共享24小时自助洗车加盟如何

共享电单车厂家

24小时共享自助洗车 6元自助洗车加盟

Linux云计算之VSFTP服务器概述-安装vsftp服务器端、客户端

学神来啦

Linux 运维

2022 CCF国际AIOps挑战赛线上宣讲会成功举办

BizSeer必示科技

TiDB HTAP 遇上新能源车企:直营模式下实时数据分析的应用实践

PingCAP

参与开源项目很难吗?

捉虫大师

研发数字化管理,如何打破“上班摸鱼下班加班”的怪圈

方云AI研发绩效

团队管理 研发管理 研发效能 数字化转型 研发管理工具

云效一站式DevOps平台

阿里云云效

云计算 阿里云 DevOps 云原生 云效

怎样搭建企业内部wiki

小炮

企业 wiki

隐私计算势头迅猛,但金融行业用户需要“冷静”

易观分析

金融 隐私计算 AMC

架构训练营 - 模块一

junl

架构实战营

阿里云数据库MongoDB版助力吉比特《一念逍遥》游戏斩获千万玩家,运营效率成倍增长

MongoDB中文社区

mongodb

自助洗车加盟需要投资多少?分析下

共享电单车厂家

自助洗车机 自助洗车加盟

英特尔陈伟:以智能边缘解锁数智时代新未来

科技新消息

在线Javascript加密混淆工具

入门小站

工具

漫谈B端的沙箱技术_Java_周爱民_InfoQ精选文章