Is It Possible to Sandbox JavaScript Running In the Browser?

前端 未结 15 665
北海茫月
北海茫月 2020-11-22 13:58

I\'m wondering if it\'s possible to sandbox JavaScript running in the browser to prevent access to features that are normally available to JavaScript code running in an HTML

相关标签:
15条回答
  • 2020-11-22 14:25

    An independent Javascript interpreter is more likely to yield a robust sandbox than a caged version of the builtin browser implementation. Ryan has already mentioned js.js, but a more up-to-date project is JS-Interpreter. The docs cover how to expose various functions to the interpreter, but its scope is otherwise very limited.

    0 讨论(0)
  • 2020-11-22 14:29

    An improved version of @RyanOHara's web workers sandbox code, in a single file (no extra eval.js file is necessary).

    function safeEval(untrustedCode)
        {
        return new Promise(function (resolve, reject)
        {
    
        var blobURL = URL.createObjectURL(new Blob([
            "(",
            function ()
                {
                var _postMessage = postMessage;
                var _addEventListener = addEventListener;
    
                (function (obj)
                    {
                    "use strict";
    
                    var current = obj;
                    var keepProperties = [
                        // required
                        'Object', 'Function', 'Infinity', 'NaN', 'undefined', 'caches', 'TEMPORARY', 'PERSISTENT', 
                        // optional, but trivial to get back
                        'Array', 'Boolean', 'Number', 'String', 'Symbol',
                        // optional
                        'Map', 'Math', 'Set',
                    ];
    
                    do {
                        Object.getOwnPropertyNames(current).forEach(function (name) {
                            if (keepProperties.indexOf(name) === -1) {
                                delete current[name];
                            }
                        });
    
                        current = Object.getPrototypeOf(current);
                    }
                    while (current !== Object.prototype);
                    })(this);
    
                _addEventListener("message", function (e)
                {
                var f = new Function("", "return (" + e.data + "\n);");
                _postMessage(f());
                });
                }.toString(),
            ")()"], {type: "application/javascript"}));
    
        var worker = new Worker(blobURL);
    
        URL.revokeObjectURL(blobURL);
    
        worker.onmessage = function (evt)
            {
            worker.terminate();
            resolve(evt.data);
            };
    
        worker.onerror = function (evt)
            {
            reject(new Error(evt.message));
            };
    
        worker.postMessage(untrustedCode);
    
        setTimeout(function () {
            worker.terminate();
            reject(new Error('The worker timed out.'));
            }, 1000);
        });
        }
    

    Test it:

    https://jsfiddle.net/kp0cq6yw/

    var promise = safeEval("1+2+3");
    
    promise.then(function (result) {
          alert(result);
          });
    

    It should output 6 (tested in Chrome and Firefox).

    0 讨论(0)
  • 2020-11-22 14:31

    I've been working on a simplistic js sandbox for letting users build applets for my site. Although I still face some challenges with allowing DOM access (parentNode just won't let me keep things secure =/), my approach was just to redefine the window object with some of its useful/harmless members, and then eval() the user code with this redefined window as the default scope.

    My "core" code goes like this... (I'm not showing it entirely ;)

    function Sandbox(parent){
    
        this.scope = {
            window: {
                alert: function(str){
                    alert("Overriden Alert: " + str);
                },
                prompt: function(message, defaultValue){
                    return prompt("Overriden Prompt:" + message, defaultValue);
                },
                document: null,
                .
                .
                .
                .
            }
        };
    
        this.execute = function(codestring){
    
            // here some code sanitizing, please
    
            with (this.scope) {
                with (window) {
                    eval(codestring);
                }
            }
        };
    }
    

    So, I can instance a Sandbox and use its execute() to get code running. Also, all new declared variables within eval'd code will ultimately bound to the execute() scope, so there will not be clashing names or messing with existing code.

    Although global objects will still be accesible, those which should remain unknown to the sandboxed code must be defined as proxies in the Sandbox::scope object.

    Hope this works for you.

    0 讨论(0)
  • 2020-11-22 14:34

    I created a sandboxing library called jsandbox that uses web workers to sandbox evaluated code. It also has an input method for explicitly giving sandboxed code data it wouldn't otherwise be able to get.

    The following is an example of the API:

    jsandbox
        .eval({
          code    : "x=1;Math.round(Math.pow(input, ++x))",
          input   : 36.565010597564445,
          callback: function(n) {
              console.log("number: ", n); // number: 1337
          }
      }).eval({
          code   : "][];.]\\ (*# ($(! ~",
          onerror: function(ex) {
              console.log("syntax error: ", ex); // syntax error: [error object]
          }
      }).eval({
          code    : '"foo"+input',
          input   : "bar",
          callback: function(str) {
              console.log("string: ", str); // string: foobar
          }
      }).eval({
          code    : "({q:1, w:2})",
          callback: function(obj) {
              console.log("object: ", obj); // object: object q=1 w=2
          }
      }).eval({
          code    : "[1, 2, 3].concat(input)",
          input   : [4, 5, 6],
          callback: function(arr) {
              console.log("array: ", arr); // array: [1, 2, 3, 4, 5, 6]
          }
      }).eval({
          code    : "function x(z){this.y=z;};new x(input)",
          input   : 4,
          callback: function(x) {
              console.log("new x: ", x); // new x: object y=4
          }
      });
    
    0 讨论(0)
  • 2020-11-22 14:34

    Where is this user JavaScript coming from?

    There is not much you can do about a user embedding code into your page and then calling it from their browser (see Greasemonkey, http://www.greasespot.net/). It's just something browsers do.

    However, if you store the script in a database, then retrieve it and eval() it, then you can clean up the script before it is run.

    Examples of code that removes all window. and document. references:

     eval(
      unsafeUserScript
        .replace(/\/\/.+\n|\/\*.*\*\/, '') // Clear all comments
        .replace(/\s(window|document)\s*[\;\)\.]/, '') // removes window. or window; or window)
     )
    

    This tries to prevent the following from being executed (not tested):

    window.location = 'http://mydomain.com';
    var w = window  ;
    

    There are a lot of limitations you would have to apply to the unsafe user script. Unfortunately, there is no 'sandbox container' available for JavaScript.

    0 讨论(0)
  • 2020-11-22 14:37

    An ugly way but maybe this works for you , I took all the globals and redefined them in the sandbox scope , as well I added the strict mode so they can't get the global object using an anonymous function.

    function construct(constructor, args) {
      function F() {
          return constructor.apply(this, args);
      }
      F.prototype = constructor.prototype;
      return new F();
    }
    // Sanboxer 
    function sandboxcode(string, inject) {
      "use strict";
      var globals = [];
      for (var i in window) {
        // <--REMOVE THIS CONDITION
        if (i != "console")
        // REMOVE THIS CONDITION -->
        globals.push(i);
      }
      globals.push('"use strict";\n'+string);
      return construct(Function, globals).apply(inject ? inject : {});
    }
    sandboxcode('console.log( this, window, top , self, parent, this["jQuery"], (function(){return this;}()));'); 
    // => Object {} undefined undefined undefined undefined undefined undefined 
    console.log("return of this", sandboxcode('return this;', {window:"sanboxed code"})); 
    // => Object {window: "sanboxed code"}
    

    https://gist.github.com/alejandrolechuga/9381781

    0 讨论(0)
提交回复
热议问题