将eval()限制在一个狭窄的范围内
我有一个JavaScript文件,读取另一个文件,可能包含需要eval()编辑的JavaScript片段。 脚本片段应该符合严格的JavaScript子集,限制了他们可以做什么以及哪些variables可以更改,但是我想知道是否有某种方法可以通过阻止eval在全局范围中看到variables来强制执行此操作。 像下面这样:
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()
不真实的代码, 真正想读取或混乱你的执行环境,你搞砸了。 但是,如果您拥有并相信所有正在执行的代码(包括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()
方式使用它。
你不能限制eval的范围
顺便看到这个职位
可能还有其他方法可以完成你想要在事物的macros伟计划中完成的任务,但是你不能以任何方式限制eval的范围。 您可以隐藏某些variables作为JavaScript中的私有variables,但我不认为这是你要去的。
这是一个想法。 如果你使用了一个静态分析器(例如你可以用esprima构build的东西)来确定被评估的代码使用哪些外部variables,并将它们别名。 通过“外部代码”我的意思是评估代码使用的variables,但不声明 。 这是一个例子:
eval(safeEval( "var x = window.theX;" +"y = Math.random();" +"eval('window.z = 500;');"))
其中safeEval返回阻止访问外部variables的上下文修改的javascriptstring:
";(function(y, Math, window) {" +"var x = window.theX;" +"y = Math.random();" +"eval(safeEval('window.z = 500;');" "})();"
现在有几件事情可以做到:
- 你可以确保被评估的代码不能读取外部variables的值,也不会写入(通过传递
undefined
的函数参数或不传递参数)。 或者你可以简单地抛出一个exception,以防止variables被不安全地访问。 - 您还要确保由eval创build的variables不会影响周围的范围
- 你可以允许eval通过在闭包之外声明这些variables而不是作为函数参数来在周围的范围中创buildvariables
- 您可以通过复制外部variables的值并将它们用作函数的参数来允许只读访问
- 您可以通过告诉safeEval不要别名这些特定的名称来允许读写访问特定的variables
- 您可以检测到eval不修改特定variables的情况,并允许它被自动排除在外(例如,在这种情况下,math不会被修改)
- 您可以通过传入与周围环境不同的参数值来给eval一个运行环境
- 您可以通过从函数返回函数参数来捕获上下文更改,以便您可以在eval外部检查它们。
请注意,使用eval
是一个特殊情况,因为从本质eval(safeEval(...))
,它不能被包装在另一个函数中(这就是为什么我们必须做eval(safeEval(...))
)。
当然,做所有这些工作可能会减慢你的代码速度,但是肯定有一些地方不是什么大问题。 希望这有助于某人。 如果有人创造了一个概念的certificate,我很乐意在这里看到一个链接。 )
有一个叫做Google Caja的项目。 你可以使用Caja“沙箱”的第三方JavaScript。 https://developers.google.com/caja/
不要使用eval
。 还有一种替代方法, js.js
JS编写的 js.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);
没有什么可怕的,你可以看到。
类似于上面的with
block方法中的dynamic函数包装脚本,这允许您将伪全局添加到要执行的代码。 您可以通过将具体内容添加到上下文来“隐藏”特定的内容。
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
。
不要执行你不信任的代码。 全局将始终可用。 如果你信任代码,你可以在它的作用域中使用特定的variables来执行它,如下所示:
(new Function("a", "b", "alert(a + b);"))(1, 2);
这相当于:
(function (a, b) { alert(a + b); })(1, 2);
Shog9♦的答案很棒。 但是,如果你的代码只是一个expression式,代码将被执行,不会返回任何内容。 对于expression式,请使用
function evalInContext(context, js) { return eval('with(context) { ' + js + ' }'); }
以下是如何使用它:
var obj = {key: true}; evalInContext(obj, 'key ? "YES" : "NO"');
它会返回"YES"
。
如果您不确定要执行的代码是expression式还是语句,可以将它们合并:
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; }