阅读 254
【前端新系列】最小化代码解密原理:前端沙箱

【前端新系列】最小化代码解密原理:前端沙箱

人这一生都应该追求什么呢?都应该追求快乐
我其实也不反对这种观念。
其实快乐是有质和量的区别,看小黄书快不快乐,看郭德纲的相声快不快乐,看莎士比亚快不快乐,好像也挺快乐对吧。如果这三种作品能够同样给你带来快乐,那你觉得哪种快乐更让你快乐。
如果现在图书馆只能留一本书,留给你的子孙后代,你会留哪一本?
---- 罗翔老师:人这一生应该追求什么呢?

image.png

主题

听了上面的语录,各位什么想法。 我的想法就是,我要开启一个新篇章了,接下来,我将以最小化的前端代码揭示一些经过层层封装让大家摸不着头脑的库和框架。

今天给大家介绍的是沙箱技术,沙箱是什么呢?Google一下:

image.png

简单点说,就是可以执行用户上传的任意代码而不影响我们本身的环境,关键就在于安全,不能让用户在我们的页面注入些不安全的内容,如果是服务器端就不能让用户触碰到我们执行环境、全局变量等等。

怎样安全的执行动态脚本?

我们都知道在JS中有一个大名顶顶的 eval,这经常让我联想到evil(邪恶),所以我们一般都不会在代码中用到。

eval('1+2')
复制代码

但是这样很危险,因为eval是全局对象的一个属性函数,执行的代码拥有着应用中其它正常代码一样的权限。所以它可以访问window对象,直接修改全局对象。很危险!

我们再看看Function

const sum = new Function('m', 'n', 'return m + n')
console.log(sum(1, 2))
复制代码

这样也可以执行成功,这样创建的函数,也可以访问局部变量和全局变量,还是不安全。

那怎么办呢?

这里要介绍一下with这个句法了。

with (expression) {
    statement
}
复制代码

这样相当于给要执行的函数指定了作用域链

image.png

有了这些知识我们就可以将它们结合起来,实现一个简单的沙箱环境,当然,这里我们还缺少一位重要的角色,你们知道是什么吗?不知道就看我下面的代码吧。


function evaluate(code, sandbox) {
  sandbox = sandbox || Object.create(null)
  const fn = new Function('sandbox', `with(sandbox){return ${code}}`)
  const proxy = new Proxy(sandbox, {
    has(target, key) {
      return true
    }
  })
  return fn(proxy)
}
复制代码

没错,通过with指定作用域链为sandbox,让函数内变量的查找沿着作用域链一层一层往上找,当查找不到时,冒泡到Proxyhas捕获器上,这里我们的策略就是让所有没有定义的变量返回true,从而达到防逃逸的效果。
如下测试用例:

evaluate('1 + 2') // 3
evaluate('console.log(1)') // Uncaught TypeError: Cannot read property 'log' of undefined
复制代码

点这里查看stackblitz在线Demo

可以看到,现在想修改全局变量,逃逸出去就没那么容易了。
怎么样,这个沙箱环境的最小化代码是不是很简单,但需要各种JS知识的储备和灵活运用。你学费了吗?

Node.js中也有vm这个包可以实现沙箱,但官方都不推荐用作沙箱了,原因是什么,如果感兴趣的小伙伴HXD比较多,我后续就再写一篇深入介绍Node.js的沙箱。

今天就到这里了,喜欢的点个赞,下期我们继续这个系列。

文章分类
前端
文章标签