Semi-sandboxing Javascript eval

前端 未结 4 2032
-上瘾入骨i
-上瘾入骨i 2021-01-12 16:03

Background: I\'m working on a framework/library to be used for a specific site in coordination with greasemonkey/userscripts. This framework/library will al

相关标签:
4条回答
  • 2021-01-12 16:44

    There's no absolute way to prevent an end user or addon developer from executing specific code in JavaScript. That's why security measures in an open source language like JavaScript is said to be foolproof (as in it's only effective against fools).

    That being said however let's build a sandbox security layer to prevent inexperienced developers from breaking your site. Personally I prefer using the Function constructor over eval to execute user code for the following reasons:

    1. The code is wrapped in an anonymous function. Hence it may be stored in a variable and called as many times as needed.
    2. The function always exists in the global scope. Hence it doesn't have access to the local variables in the block which created the function.
    3. The function may be passed arbitrary named parameters. Hence you may exploit this feature to pass or import modules required by the user code (e.g. jQuery).
    4. Most importantly you may set a custom this pointer and create local variables named window and document to prevent access to the global scope and the DOM. This allows you to create your own version of the DOM and pass it to the user code.

    Note however that even this pattern has disadvantages. Most importantly it may only prevent direct access to the global scope. User code may still create global variables by simply declaring variables without var, and malicious code may use hacks like creating a function and using it's this pointer to access the global scope (the default behavior of JavaScript).

    So let's look at some code: http://jsfiddle.net/C3Kw7/

    0 讨论(0)
  • 2021-01-12 16:45

    You can do something like this: http://jsfiddle.net/g68NP/

    Problem is that you'll have to add a lot of code to protect every property, every native method, etc. The meat of the code really comes down to using __defineGetter__, whose support is limited. Since you're probably not running this on IE, you should be fine.

    EDIT: http://jsfiddle.net/g68NP/1/ This code will make all properties read-only. The use of hasOwnProperty() may or may not be desirable.

    In case JSFiddle goes down:

    function safeEval(input) {
        // Remove eval and evalJS from the window:
        var e = [window.eval, window.evalJS, document.getElementById], a;
        window.eval = function(){};
        window.evalJS = function(){};
        document.getElementById = function (id) {
            var elem = (e[2]).call(document, id);
            for (var prop in elem) {
                if (elem.hasOwnProperty(prop)) {
                    elem.__defineGetter__(prop, function () {
                        return (function (val) {
                            return val;
                        }(elem[prop]));
                    });
                }                
            }
            return elem;
        };
    
        try {
            /* More sanition needed before being passed to eval */
    
            // Eval the input, stuffed into an annonomous function
            // so the code to be evalued can not access the stored
            // eval functions:
            a = (e[0])("(function(){return " + input + "}())");
        } catch(ex){}
    
        // Return eval and evalJS to the window:
        window.eval = e[0];
        window.evalJS = e[1];
        document.getElementById = e[2];
    
        // Return the eval'd result
        return a;
    }
    
    0 讨论(0)
  • 2021-01-12 16:58

    If all you want is a simple getter, program one instead of trying to eval anything.

    function get(input) {
        // if input is a string, it will be used as a DOM selector
        // if it is an array (of strings), they will access properties of the global object
        if (typeof input == "string")
            return document.querySelector(input);
        else if (Array.isArray(input)) {
            var res = window;
            for (var i=0; i<input.length && typeof input[i] == "string" && res; i++)
                res = res[input[i]];
            return res;
        }
        return null;
    }
    
    0 讨论(0)
  • 2021-01-12 17:00

    I know this is an old post, but I just want to share an upgraded version of the Aadit M Shah solution, that seems to really sandbox without any way to access window (or window children): http://jsfiddle.net/C3Kw7/20/

    // create our own local versions of window and document with limited functionality
    
    var locals = {
        window: {
        },
        document: {
        }
    };
    
    var that = Object.create(null); // create our own this object for the user code
    var code = document.querySelector("textarea").value; // get the user code
    var sandbox = createSandbox(code, that, locals); // create a sandbox
    
    sandbox(); // call the user code in the sandbox
    
    function createSandbox(code, that, locals) {
        code = '"use strict";' + code;
        var params = []; // the names of local variables
        var args = []; // the local variables
    
        var keys = Object.getOwnPropertyNames( window ),
        value;
    
        for( var i = 0; i < keys.length; ++i ) {
            //console.log(keys[i]);
            locals[keys[i]] = null;  
        }
    
        delete locals['eval'];
        delete locals['arguments'];
    
        locals['alert'] = window.alert; // enable alert to be used
    
        for (var param in locals) {
            if (locals.hasOwnProperty(param)) {
                args.push(locals[param]);
                params.push(param);
            }
        }
    
        var context = Array.prototype.concat.call(that, params, code); // create the parameter list for the sandbox
        //console.log(context);
        var sandbox = new (Function.prototype.bind.apply(Function, context)); // create the sandbox function
        context = Array.prototype.concat.call(that, args); // create the argument list for the sandbox
    
        return Function.prototype.bind.apply(sandbox, context); // bind the local variables to the sandbox
    }
    
    0 讨论(0)
提交回复
热议问题