8

背景:我正在开发一个框架/库,用于与greasemonkey/userscripts 协调的特定站点。这个框架/库将允许插件支持。它的工作方式是在库中注册一个插件,列出所需的页面、资源等,并且该库将等到满足所有条件才能调用插件的load()函数。

问题:在这个“必需的东西”列表中,我希望插件开发人员能够指定要评估为“必需资源”的 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/用户脚本。我没有直接访问权限来更改站点,或者它是 javascript。
for 的输入safeEval()可以是任何有效的 javascript,可以是 DOM 查询,也可以是简单的求值,只要它不改变窗口对象或 DOM。

4

4 回答 4

7

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

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

  1. 代码被包装在一个匿名函数中。因此,它可以存储在一个变量中,并根据需要多次调用。
  2. 该函数始终存在于全局范围内。因此,它无法访问创建函数的块中的局部变量。
  3. 该函数可以传递任意命名参数。因此,您可以利用此功能来传递或导入用户代码(例如jQuery)所需的模块。
  4. 最重要的是,您可以设置一个自定义指针并创建名为and的this局部变量,以防止访问全局范围和 DOM。这允许您创建自己的 DOM 版本并将其传递给用户代码。windowdocument

但是请注意,即使这种模式也有缺点。最重要的是,它可能只会阻止直接访问全局范围。var用户代码仍然可以通过简单地声明不带this.

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

于 2012-09-23T17:54:05.583 回答
0

如果您想要的只是一个简单的 getter,请编写一个而不是尝试评估任何东西。

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;
}
于 2012-09-23T16:51:53.230 回答
0

你可以这样做:http: //jsfiddle.net/g68NP/

问题是您必须添加大量代码来保护每个属性、每个本机方法等。代码的实质归结为 using __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;
}
于 2012-09-23T17:16:33.413 回答
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
}
于 2015-01-15T16:09:47.707 回答