js 沙箱(eval, with)

1,512 阅读2分钟

直接嵌套

这种方式说起来并不是什么特别好的点子,因为需要花费比较多的精力在安全性上.

eval执行

最简单的方式,就是使用eval进行代码的执行

eval('console.log("a simple script");');
 

但,如果你是直接这么使用的话, congraduations... do die... 因为,eval 的特性是如果当前域里面没有,则会向上遍历.一直到最顶层的global scope 比如window.以及,他还可以访问closure内的变量.看demo:

function Auth(username)
{
  var password = "trustno1";
  this.eval = function(name) { return eval(name) } // 相当于直接this.name
}
 
auth = new Auth("Mulder")
console.log(auth.eval("username")); // will print "Mulder"
console.log(auth.eval("password")); // will print "trustno1"
 

那有没有什么办法可以解决eval这个特性呢? 答: 没有. 除非你不用 ok,那我就不用. 我们这里就可以使用new Function(..args,bodyStr) 来代替eval.

new Function

new Function就是用来,放回一个function obj的. 用法参考:new Function. 所以,上面的代码,放在new Function中,可以写为:

new Function('console.log("a simple script");')();
 

这样做在安全性上和eval没有多大的差别,不过,他不能访问closure的变量,即通过this来调用,而且他的性能比eval要好很多. 那有没有办法解决global var的办法呢? 有啊... 只是有点复杂先用with,在用Proxy

with

with这个特性,也算是一个比较鸡肋的,他和eval并列为js两大SB特性. 不说无用, bug还多,安全性就没谁了... 但是, with的套路总是有人喜欢的.在这里,我们就需要使用到他的特性.因为,在with的scope里面,所有的变量都会先从with定义的Obj上查找一遍.

var a = {
    c:1
}
var c =2;
with(a){
    console.log(c); //等价于c.a
}
 

所以,第一步改写上面的new Function(),将里面变量的获取途径控制在自己的手里.

function compileCode (src) {  
  src = 'with (sandbox) {' + src + '}'
  return new Function('sandbox', src)
}

这样,所有的内容多会从sandbox这个str上面获取,但是找不到的var则又会向上进行搜索. 为了解决这个问题,则需要使用: proxy

proxy

es6 提供的Proxy特性,说起来也是蛮牛逼的. 可以将获取对象上的所有方式改写.具体用法可以参考: 超好用的proxy. 这里,我们只要将has给换掉即可. 有的就好,没有的就返回undefined

function compileCode (src) {
  src = 'with (sandbox) {' + src + '}'
  const code = new Function('sandbox', src)
 
  return function (sandbox) {
    const sandboxProxy = new Proxy(sandbox, {has})
    return code(sandboxProxy)
  }
}
 
// 相当于检查 获取的变量是否在里面 like: 'in'
function has (target, key) {
  return true
}
 
compileCode('log(name)')(console);
 

这样的话,就能完美的解决掉 向上查找变量的烦恼了. 另外一些大神,发现在新的ECMA里面,有些方法是不会被with scope 影响的. 这里,主要是通过Symbol.unscopables 这个特性来检测的

blog.csdn.net/chenxie1213…