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

Javascript 将eval()限制在狭窄的范围内,javascript,eval,Javascript,Eval,我有一个javascript文件,它读取另一个文件,该文件可能包含需要进行eval()-ed的javascript片段。脚本片段应该符合javascript的严格子集,该子集限制了它们可以做什么以及可以更改哪些变量,但我想知道是否有某种方法可以通过阻止eval看到全局范围内的变量来实现这一点。如下所示: function safeEval( fragment ) { var localVariable = g_Variable; { // do magic sc

我有一个javascript文件,它读取另一个文件,该文件可能包含需要进行eval()-ed的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 );
    }
}

实际代码不需要像这样——我对闭包等任何奇怪的技巧都持开放态度,但我确实想知道这是否可行。

您不能限制eval的范围

顺便说一句,见


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

简短回答::不。如果它在全局范围内,任何东西都可以使用它

详细回答:如果你正在
eval()
处理那些真正想读取或破坏执行环境的不可信代码,你就完蛋了。但是,如果您拥有并信任正在执行的所有代码,包括正在执行的
eval()
ed,则可以通过覆盖执行上下文来伪造代码:

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()
的任何其他方式使用它)


有一个项目叫做Google Caja。您可以使用Caja“沙箱”第三方javascript

这里有一个想法。如果您使用静态分析器(例如,您可以使用它来构建)来确定eval代码使用哪些外部变量,并为它们设置别名,该怎么办。所谓“外部代码”,我指的是评估代码使用但不声明的变量。下面是一个例子:

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通过在闭包外部声明变量而不是作为函数参数来在周围的范围中创建变量
  • 通过复制外部变量的值并将其用作函数的参数,可以允许只读访问
  • 您可以通过告诉SafeVal不要别名这些特定名称来允许对特定变量进行读写访问
  • 您可以检测eval未修改特定变量的情况,并允许自动排除该变量的别名(例如,在本例中,未修改数学)
  • 您可以通过传入可能不同于周围上下文的参数值,为eval提供一个要在其中运行的上下文
  • 您还可以通过从函数返回函数参数来捕获上下文更改,以便在eval之外检查它们
请注意,
eval
的使用是一种特殊情况,因为从本质上讲,它不能有效地包装在另一个函数中(这就是为什么我们必须执行
eval(safeEval(…)


当然,完成所有这些工作可能会降低代码的速度,但在某些地方,成功并不重要。希望这对别人有帮助。如果有人创建了一个概念证明,我希望在这里看到它的链接;)

不要使用
eval
。还有另一种选择,
js.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);

正如您所看到的,没有什么可怕的。

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

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

有关示例,请参见。请注意,并非所有浏览器都支持
Object.keys

不要执行您不信任的代码。Globals总是可以访问的。 如果您确实信任代码,则可以使用其作用域中的特定变量执行代码,如下所示:

(new Function("a", "b", "alert(a + b);"))(1, 2);
这相当于:

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

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

我意外地发现我可以使用代理来限制scope对象,将变量屏蔽在范围之外似乎要容易得多。我不确定这种方法是否有缺点,但到目前为止,它对我很有效

函数maskedEval(src,ctx={}) { ctx=新代理(ctx{ has:()=>true }) //在私有上下文中执行脚本 设func=(新函数(“with(this){“+src+”}”); 函数调用(ctx); }
a=1;
maskedEval(“console.log(a)”,{console:console});
maskedEval(“console.log(a)”,{console:console,a:22});
马斯克德瓦尔(“a=1,{a:22}”)
控制台日志(a)

我不知道它是否适用于
eval
,但你可以尝试切换它的执行上下文(google激活对象)。看看dojox.secure.sandbox实现。现在,你可能想要的是一个Web Worker.3MB,gzip之后只有594KBcompression@user6457151)尝试使用闭包编译器对其进行进一步压缩。2)