代码之家  ›  专栏  ›  技术社区  ›  JSBÕ±Õ¸Õ£Õ¹

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

  •  29
  • JSBÕ±Õ¸Õ£Õ¹  · 技术社区  · 16 年前

    我有一个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 );
        }
    }
    

    可能的 .

    8 回复  |  直到 16 年前
        1
  •  56
  •   Universal Electricity    9 年前

    :否。如果它在全局范围内,则它可用于任何对象。

    长话短说 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() ).

        2
  •  7
  •   Ge Liu    7 年前

    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
  •  6
  •   Dale Anderson    10 年前

    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 并非所有浏览器都支持。

        4
  •  3
  •   Community CDub    7 年前

    你不能限制评估的范围

    顺便说一句,见 this post

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

        5
  •  3
  •   Igor Klopov    11 年前

    有一个项目叫做Google Caja。您可以使用Caja“沙箱”第三方javascript。 https://developers.google.com/caja/

        6
  •  3
  •   B T    11 年前

    有个主意。如果您使用静态分析器(您可以使用它构建)会怎么样 esprima ,例如)来确定评估代码使用哪些外部变量,并为它们设置别名。所谓“外部代码”,我指的是评估代码 但事实并非如此 声明

    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(...))

        7
  •  2
  •   polkovnikov.ph    10 年前

    不要使用 eval js.js : JS 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);
    

    没什么可怕的,正如你所见。

        8
  •  2
  •   Paul Beard    10 年前

    不要执行你不信任的代码。Globals总是可以访问的。

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

    这相当于:

    (function (a, b) {
        alert(a + b);
    })(1, 2);
    
        9
  •  1
  •   LaysDragon    3 年前

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

    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)