最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 如何禁止开发者操作网页上的DOM对象?

    正文概述 掘金(字节前端)   2021-03-09   490

    通常情况下,在一个HTML页面上,我们总能够通过DOM API访问想要访问的HTML元素,进行操作。

    如果基于某种原因,允许用户注入代码到网页上,但又要禁止用户对DOM对象进行操作,即只允许用户调用我们提供的API,不允许用户通过注入的JS来修改我们创建的UI组件甚至整个网页内容,那么我们要怎么做呢?

    我们基本上无法通过JS来禁止用户通过注入的JS操作DOM,因为window对象、document对象这些对象以及它们的API是无法通过JS改写的:

    window.document = 111;
    console.log(window.document); // #document
    

    如果我们用Object.getOwnPropertyDescriptor查看,会发现window.document实际上是一个getter,而且它的configurable是false。

    Object.getOwnPropertyDescriptor(window, 'document');
    // {get: ƒ, set: undefined, enumerable: true, configurable: false}
    

    即使我们对允许用户合法注入的JS外面包裹函数作用域,我们还是无法彻底阻止用户访问document和window对象。

    (function(window, document) {
      // user code
      console.log(this, window, document); // {}, null, null
      const win = (function () {
        return this;
      }());
      console.log(win); // Window
      // ---
    }).call({}, null, null)
    

    比如说上面的代码,我们在用户注入的代码前后增加包装代码,把window和document对象通过函数参数覆盖,我们还是能通过函数调用的this拿到window对象。

    要防住这个漏洞,我们可以在包装的时候使用严格模式:

    (function(window, document) {'use strict'
      // user code
      console.log(this, window, document); // {}, null, null
      const win = (function () {
        return this;
      }());
      console.log(win); // undefined
      // ---
    }).call({}, null, null)
    

    但是这个依然不解决问题:

    (function(window, document) {'use strict'
      console.log(this, window, document); // {}, null, null
      const win = (function () {
        return this;
      }());
      console.log(win);   // undefined
      setTimeout(function() {
        console.log(this);  // Window
      });
    }).call({}, null, null)
    

    所以结论是包装代码无法让用户彻底无法访问window和document对象。

    那么我们要怎么做既能合法让用户注入代码实现功能,又能够隔离window和document对象呢?

    用worker做沙箱

    第一种办法是只允许用户的代码跑在worker里。

    我们知道worker的环境是和浏览器环境互相独立的线程,所以跑在worker里的代码是不能访问window和document对象的,这样就保证了安全性。

    function execCodeInWorker(code) {
      const blob = new Blob([code]);
      const url = URL.createObjectURL(blob);
      
      const worker = new Worker(url);
      return worker;
    }
    
    const userCode = `
      console.log(typeof window, typeof document); // undefined undefined
    `;
    
    execCodeInWorker(userCode);
    

    使用worker的问题是,当worker和浏览器环境通讯时,需要采用postMessage,如果有较多的交互操作,性能开销比较大,而且对写代码的开发者有使用成本。

    使用Shadow DOM

    如果我们只是不允许用户注入的JS修改UI,我们也可以将整个UI通过Shadow DOM渲染,并且将ShadowRoot的模式设置为closed封闭起来,这样的话,用户就无法拿到Shadow DOM的ShadowRoot对象,从而无法进行操作。

    下面是一个简单的例子:

    (function () {
      const root = document.body.attachShadow({mode: 'closed'});
      
      let list;
    
      function init() {
        root.innerHTML = `
          <h1>Todo List</h1>
          <ul></ul>
        `
        list = root.querySelector('ul');
      }
      
      function addTask(desc) {
        const task = document.createElement('li');
        task.textContent = desc;
        list.appendChild(task);
        return list.children.length - 1;
      }
      
      function removeTask(index) {
        const task = list.children[index];
        if(task) task.remove();
      }
      
      window.init = init;
      window.addTask = addTask;
      window.removeTask = removeTask;
    }());
    
    init();
    addTask('task1');
    

    我们通过document.body.attachShadow({mode: 'closed'});创建ShadowRoot,通过Shadow DOM API来创建UI,因为这个root对象我们没有暴露给用户,而且mode是closed,所以用户拿不到对象,无法通过DOM操作我们的UI,只能通过我们暴露给用户的addTask和removeTask来操作。

    ?注意,用户当然仍可以通过DOM API来往body中插入其他内容,但是,当一个元素创建了Shadow DOM,浏览器会优先渲染Shadow DOM,而忽略它的其他子元素,所以用户往body中插入任何内容都不会被渲染出来。唯一的例外是如果插入script标签,脚本会被执行,但是我们可以简单通过防止xss的代码过滤来阻止用户插入script标签。

    这样,我们就通过Shadow DOM获得了一个相对安全的环境,对比worker的方式,可以避免postMessage的开销和拥有更简单的写法。当然Shadow DOM也有弊端,比如用户虽然不能改写当前body元素中渲染的内容了,但是可以彻底删掉body元素重新创建一个:

    document.documentElement.removeChild(body);
    const newBody = document.createElement('body');
    document.documentElement.appendChild(newBody);
    

    但是那样的话,原body中的所有内容也需要重建了。因此,使用Shadow DOM API至少大大增加了用户侵入的成本。

    关于禁止开发者操作DOM对象的话题就谈到这里,还有什么可行的方法或者大家有什么想补充的,欢迎在 issue 中讨论。


    欢迎关注「 字节前端 ByteFE 」

    简历投递联系邮箱「 tech@bytedance.com 」


    起源地下载网 » 如何禁止开发者操作网页上的DOM对象?

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元