半沙盒Javascript评估

时间:2012-09-08 21:29:54

标签: javascript eval greasemonkey sandbox

背景:我正在开发一个框架/库,用于与greasemonkey / userscripts协调的特定网站。该框架/库将允许插件支持。它的工作方式是在库中添加一个插件寄存器,列出所需的页面,资源,ectera,库将等到满足所有的critera以调用addon的load()函数。

问题:在这个“必需的东西”列表中,我希望addon devs能够指定javascript(作为字符串)作为“必需资源”进行评估。例如'document.getElementById("banana")'。我想要做的是半沙盒评估“必需资源”,以便评估可以访问窗口& DOM对象但无法直接更改它们。我也想制作eval,并且沙盒中无法访问evalJS。


实施例

  • document.getElementById("banana") - >有效
  • document.getElementById("apple).id = "orange" - >无效
  • window.grape - >有效
  • window.grape = 'potato' - >无效
  • (someObj.applesCount > 0 ? 'some' : 'none') - >有效


到目前为止我有什么

function safeEval(input) {

    // Remove eval and evalJS from the window:
    var e = [window.eval, window.evalJS], a;
    window.eval = function(){};
    window.evalJS = function(){};

    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];

    // Return the eval'd result
    return a;
}


备注
这是Greasemonkey / userscript。我没有直接访问权限来改变网站,或者是javascript safeEval()的输入可以是任何有效的javascript,无论是DOM查询还是简单的评估,只要它不会改变窗口对象或DOM。

4 个答案:

答案 0 :(得分:5)

没有绝对的方法可以阻止最终用户或插件开发人员在JavaScript中执行特定代码。这就是为什么像JavaScript这样的开源语言中的安全措施被认为是万无一失的(因为它只对傻瓜有效)。

尽管如此,让我们构建一个沙盒安全层,以防止没有经验的开发人员破坏您的网站。我个人更喜欢使用Function构造函数而不是eval来执行用户代码,原因如下:

  1. 代码包含在匿名函数中。因此,它可以存储在变量中,并根据需要多次调用。
  2. 该功能始终存在于全局范围内。因此,它无法访问创建函数的块中的局部变量。
  3. 该函数可以传递任意命名参数。因此,您可以利用此功能来传递或导入用户代码所需的模块(例如jQuery)。
  4. 最重要的是,您可以设置自定义this指针并创建名为windowdocument的局部变量,以防止访问全局范围和DOM。这允许您创建自己的DOM版本并将其传递给用户代码。
  5. 但请注意,即使是这种模式也有缺点。最重要的是,它可能只会阻止直接访问全球范围。用户代码仍然可以通过简单地声明没有var的变量来创建全局变量,恶意代码可能会使用诸如创建函数之类的黑客并使用它的this指针来访问全局范围(JavaScript的默认行为)。

    让我们看看一些代码:http://jsfiddle.net/C3Kw7/

答案 1 :(得分:0)

如果您只想要一个简单的吸气剂,请编程一个而不是试图评估任何东西。

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;
}

答案 2 :(得分:0)

您可以执行以下操作:http://jsfiddle.net/g68NP/

问题是你必须添加大量代码来保护每个属性,每个本机方法等等。代码的内容实际上归结为使用__defineGetter__,其支持是有限的。既然你可能没有在IE上运行它,你应该没问题。

编辑:http://jsfiddle.net/g68NP/1/此代码将使所有属性都是只读的。使用hasOwnProperty()可能是也可能不合适。

如果JSFiddle失败:

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;
}

答案 3 :(得分:0)

我知道这是一篇旧帖子,但我只是想分享 Aadit M Shah 解决方案的升级版本,这似乎真的是沙盒而无法访问窗口(或窗口子窗口) :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
}