【问题标题】:Restricting eval() to a narrow scope将 eval() 限制在一个狭窄的范围内
【发布时间】:2010-10-07 07:27:06
【问题描述】:

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

实际的代码不需要看起来像这样——我对任何和所有关于闭包的奇怪技巧都持开放态度,等等。但我确实想知道这是否可能

【问题讨论】:

  • 我不知道它是否适用于eval,但你可以尝试切换它的执行上下文(google激活对象)。
  • 看看 dojox.secure.sandbox 的实现。
  • 现在,您可能想要的是一个 Web Worker。

标签: javascript eval


【解决方案1】:

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

长答案:如果你eval()ing 不可信的代码真的想阅读或弄乱你的执行环境,那你就完蛋了。但是,如果您拥有并信任所有正在执行的代码,包括 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() 的方式使用它)。

【讨论】:

  • with 方法有一个小问题,我以前很讨厌。在 with 块中声明的函数无法访问 with 语句的目标。
  • 阅读最后的警告,@meph - 不要喂这个不是你写/控制的东西。
  • 你能举个例子吗,这怎么可能结束?我确实检查了一些,但没有找到一些......我将阻止变量定义,如 var test = 1
  • 我检查表达式是否包含 = 并且如果下一个字符不是 = 将阻止它:DI 必须检查更多表达式,但我不知道还要尝试哪个.. 我想得到一些不良表达的例子
  • 我能想到的最简单的坏表达是:(function() { this.document.anythingYouWant... }).call(null);当调用不带“this”参数时,“this”成为全局上下文。
【解决方案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;
}

【讨论】:

  • 哇,这真是令人印象深刻。
【解决方案3】:

类似于上面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】:

    不能限制eval的范围

    顺便说一句,请参阅this post

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

    【讨论】:

      【解决方案5】:

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

      【讨论】:

        【解决方案6】:

        这是一个想法。如果您使用静态分析器(例如,您可以使用 esprima 构建的东西)来确定 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;');"
        "})();"
        

        你现在可以用这个做几件事:

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

        请注意,eval 的使用是一种特殊情况,因为就其性质而言,它实际上不能被包装在另一个函数中(这就是我们必须使用eval(safeEval(...)) 的原因)。

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

        【讨论】:

          【解决方案7】:

          不要使用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);
          

          如您所见,没什么可怕的。

          【讨论】:

          • 3MB,gzip 压缩后只有 594KB
          • @user645715 1) 尝试使用 Closure Compiler 进一步压缩它。 2)有 200 倍的减速,这比 3MB 源代码差得多。 3) 目前还没有其他解决方案。
          【解决方案8】:

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

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

          这相当于:

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

          【讨论】:

            【解决方案9】:

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

            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)
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2019-08-15
              • 1970-01-01
              • 1970-01-01
              • 2015-04-06
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多