[英]Restricting eval() to a narrow scope
我有一個 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()
輸入真正想要讀取或弄亂您的執行環境的不受信任的代碼,那么您就完蛋了。 但是,如果您擁有並信任所有正在執行的代碼,包括被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()
它(或將其傳遞給 newFunction()
,或以任何其他行為類似於eval()
的方式使用它)。
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;
}
與上述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
。
有一個名為 Google Caja 的項目。 您可以使用 Caja “沙盒”第三方 javascript。 https://developers.google.com/caja/
這是一個想法。 如果您使用靜態分析器(例如,您可以使用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;');"
"})();"
你現在可以用這個做幾件事:
undefined
作為函數參數傳遞,或不傳遞參數)。 或者,在不安全地訪問變量的情況下,您可以簡單地拋出異常。 請注意,使用eval
是一種特殊情況,因為就其性質而言,它實際上不能被包裝在另一個函數中(這就是我們必須執行eval(safeEval(...))
的原因)。
當然,完成所有這些工作可能會減慢您的代碼速度,但肯定有一些地方的命中無關緊要。 希望這可以幫助某人。 如果有人創建了概念證明,我很樂意在這里看到它的鏈接; )
不要執行你不信任的代碼。 全局變量將始終可訪問。 如果您確實信任該代碼,則可以使用其范圍內的特定變量執行它,如下所示:
(new Function("a", "b", "alert(a + b);"))(1, 2);
這相當於:
(function (a, b) {
alert(a + b);
})(1, 2);
你不能限制 eval 的范圍
順便說一句,看到這個帖子
在宏偉的計划中,可能有其他方法可以完成您想要完成的事情,但您不能以任何方式限制 eval 的范圍。 您也許可以在 javascript 中將某些變量隱藏為偽私有變量,但我認為這不是您想要的。
不要使用eval
。 還有一個替代方案, 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);
如您所見,沒什么可怕的。
我偶然發現我可以使用 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 });
maskedEval("console.log(a)", { console, a: 22});
maskedEval("a = 1", { a: 22 })
console.log(a)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.