将eval()限制在狭窄的范围内

时间:2009-02-12 21:53:40

标签: javascript eval

我有一个javascript文件读取另一个文件,其中可能包含需要eval()编辑的javascript片段。脚本片段应该符合javascript的严格子集,这限制了它们可以做什么以及它们可以更改哪些变量,但我想知道是否有某种方法可以通过阻止eval在全局范围内查看变量来强制执行此操作。如下所示:

function safeEval( fragment )
{
    var localVariable = g_Variable;

    {
        // do magic scoping here so that the eval fragment can see localVariable
        // but not g_Variable or anything else outside function scope

        eval( fragment );
    }
}

实际代码不需要看起来像这样 - 我可以使用闭包等任何和所有奇怪的技巧。但我确实想知道这是否是可能的

9 个答案:

答案 0 :(得分:52)

简短回答:不会。如果它在全球范围内,它可以用于任何事情。

长答案:如果您eval() 确实希望阅读或弄乱您的执行环境的不受信任的代码,那么您就搞砸了。但是,如果您拥有并信任所有正在执行的代码,包括eval()编辑的代码,您可以通过覆盖执行上下文来伪造它:

function maskedEval(scr)
{
    // set up an object to serve as the context for the code
    // being evaluated. 
    var mask = {};
    // mask global properties 
    for (p in this)
        mask[p] = undefined;

    // execute script in private context
    (new Function( "with(this) { " + scr + "}")).call(mask);
}

同样,我必须强调:

  

这仅用于屏蔽可信代码以及执行该代码的上下文。如果您不相信代码,请不要eval()它(或将其传递给新的Function(),或以任何其他行为方式使用eval()。)

答案 1 :(得分:3)

这是一个想法。如果您使用静态分析器(例如,您可以使用esprima构建的东西)来确定eval'd代码使用哪些外部变量,并将它们作为别名。 “外部代码”是指eval'd代码使用但不声明的变量。这是一个例子:

eval(safeEval(
     "var x = window.theX;"
    +"y = Math.random();"
    +"eval('window.z = 500;');"))

其中safeEval返回使用阻止访问外部变量的上下文修改的javascript字符串:

";(function(y, Math, window) {"
  +"var x = window.theX;"
  +"y = Math.random();"
  +"eval(safeEval('window.z = 500;');"
"})();"

现在你可以做几件事:

  • 您可以确保eval'd代码无法读取外部变量的值,也不能写入它们(通过传递undefined作为函数参数,或者不传递参数)。或者,您可以在不安全地访问变量的情况下抛出异常。
  • 您还确保eval创建的变量不会影响周围范围
  • 您可以通过在闭包之外声明这些变量而不是函数参数来允许eval在周围范围内创建变量
  • 您可以通过复制外部变量的值并将它们用作函数的参数来允许只读访问
  • 您可以通过告诉safeEval不为这些特定名称添加别名来允许对特定变量的读写访问
  • 您可以检测到eval 修改特定变量并允许其自动排除别名的情况(例如,在这种情况下,数学未被修改)
  • 您可以通过传入可能不同的参数值来为eval提供运行的上下文
  • 您还可以通过从函数返回函数参数来捕获上下文更改,以便您可以在eval之外检查它们。

请注意,使用eval是一种特殊情况,因为从本质上讲,它实际上不能包含在另一个函数中(这就是为什么我们必须eval(safeEval(...)))。

当然,完成所有这些工作可能会减慢您的代码速度,但肯定有一些地方无法解决问题。希望这有助于某人。如果有人创建概念证明,我很乐意在这里看到它的链接; )

答案 2 :(得分:3)

Shog9♦的答案很棒。但是如果你的代码只是一个表达式,代码将被执行,不会返回任何内容。对于表达式,请使用

function evalInContext(context, js) {

  return eval('with(context) { ' + js + ' }');

}

以下是如何使用它:

var obj = {key: true};

evalInContext(obj, 'key ? "YES" : "NO"');

它将返回"YES"

如果您不确定要执行的代码是表达式还是语句,可以将它们组合起来:

function evalInContext(context, js) {

  var value;

  try {
    // for expressions
    value = eval('with(context) { ' + js + ' }');
  } catch (e) {
    if (e instanceof SyntaxError) {
      try {
        // for statements
        value = (new Function('with(this) { ' + js + ' }')).call(context);
      } catch (e) {}
    }
  }

  return value;
}

答案 3 :(得分:2)

您无法限制评估范围

顺便提一下this post

可能还有其他方法可以完成你想要在宏观方案中完成的事情,但是你不能以任何方式限制eval的范围。您可以在javascript中将某些变量隐藏为伪私有变量,但我不认为这是您的目的。

答案 4 :(得分:2)

与上面with块方法中的动态函数包装脚本类似,这允许您将伪全局变量添加到要执行的代码中。您可以通过将特定事物添加到上下文中来“隐藏”特定事物。

function evalInContext(source, context) {
    source = '(function(' + Object.keys(context).join(', ') + ') {' + source + '})';

    var compiled = eval(source);

    return compiled.apply(context, values());

    // you likely don't need this - use underscore, jQuery, etc
    function values() {
        var result = [];
        for (var property in context)
            if (context.hasOwnProperty(property))
                result.push(context[property]);
        return result;
    }
}

有关示例,请参阅http://jsfiddle.net/PRh8t/。请注意,并非所有浏览器都支持Object.keys

答案 5 :(得分:1)

有一个名为Google Caja的项目。你可以使用Caja“沙盒”第三方javascript。 https://developers.google.com/caja/

答案 6 :(得分:1)

请勿使用eval。还有一个替代方法js.jsJS interpreter written in JS,这样您就可以在已设法设置的任何环境中运行JS程序。以下是项目页面中API的示例:

var jsObjs = JSJS.Init();
var rval = JSJS.EvaluateScript(jsObjs.cx, jsObjs.glob, "1 + 1");
var d = JSJS.ValueToNumber(jsObjs.cx, rval);
window.alert(d); // 2
JSJS.End(jsObjs);

没有什么可怕的,正如你所看到的那样。

答案 7 :(得分:1)

我偶然发现我可以使用代理来限制作用域对象,似乎更容易将变量屏蔽在作用域之外。我不确定这种方法是否有缺点,但到目前为止它对我来说效果很好。

function maskedEval(src,ctx={})
{
    ctx = new Proxy(ctx,{
        has:()=>true
    })
    // execute script in private context
    let func = (new Function( "with(this) { " + src + "}"));
    func.call(ctx);
}
a = 1;
maskedEval("console.log(a)",{console:console});
maskedEval("console.log(a)",{console:console,a:22});

maskedEval("a = 1",{a:22})
console.log(a)

答案 8 :(得分:0)

不要执行您不信任的代码。 Globals始终可以访问。 如果您确实信任该代码,则可以使用其范围内的特定变量执行它,如下所示:

(new Function("a", "b", "alert(a + b);"))(1, 2);

这相当于:

(function (a, b) {
    alert(a + b);
})(1, 2);