Javascript 在这种情况下eval()是邪恶的吗?

Javascript 在这种情况下eval()是邪恶的吗?,javascript,security,eval,Javascript,Security,Eval,您好:)我想知道在这种情况下,eval是否安全: !function(){ var a = 1; !function(){ var b = 2; try { eval.call({}, 'a'); } catch (e) { console.log(e.message); } try { eval.call({}, 'b'); } catch (e) { console.log(e.message

您好:)我想知道在这种情况下,
eval
是否安全:

!function(){
    var a = 1;
    !function(){
        var b = 2;
        try { eval.call({}, 'a'); } 
        catch (e) { console.log(e.message); }
        try { eval.call({}, 'b'); } 
        catch (e) { console.log(e.message); }
    }();
}();
两个电话都失败了。这是否意味着
eval
无法访问周围的范围?换句话说,使用
eval.call({},…')
而不是
eval('…')
,可以
eval
只访问对象的成员吗



这里的一些测试:.

如果
eval
没有“调用上下文”(基本上是
this
的局部值),那么执行上下文就是全局上下文

因此,这应该是可行的:

!function(){
    var a = 1;
    function evalWrapper() {
        var b = 2;
        try { eval('a'); console.log("yaay!"); } 
        catch (e) { console.log(e.message); }
    };
    evalWrapper.call({});
}();
规范中描述的第一个场景是:

如果没有调用上下文,或者没有通过对eval函数的直接调用(15.1.2.1.1)计算eval代码,则,
a。如10.4.1.1所述,使用评估代码C初始化执行上下文,如同它是全局执行上下文一样

因此,除了直接调用
eval(something)
,您所做的任何事情都包括,如果您阅读了引用的部分,则通过名称“eval”以外的其他名称引用“eval”函数然后在全局上下文(包括全局词汇上下文)中计算字符串中的代码。因此,代码似乎根本不在周围函数中。

附加测试 尝试从IIFE内部访问变量

var a = 1;
!function(){
    var b = 2;
    try { eval.call(null, 'console.log(a)'); } 
    catch (e) { console.log(e.message); }
    try { eval.call(null, 'console.log(b)'); } 
    catch (e) { console.log(e.message); }
    try { eval.call({ c: 3 }, 'console.log(c)'); } 
    catch (e) { console.log(e.message); }
}();
铬:

1
b没有定义
c没有定义
跨浏览器测试 尝试从IIFE外部访问变量。考虑到Pointy的答案,除了
a
之外的所有变量都应该是可访问的

!function () {
    var a = 1;
    eval.call(window, [
        'b = 2;',
        'c = function () { return 3; };',
        'd = function d() { return 4; };',
        'function e() { return 5; };',
        'x = function () { return a; };'
    ].join(' '));
}();
var l = function (a) { console.log(a); };
try { l('window.a -> ' + window.a); } catch(e) { l(e.message); };
try { l('window.b -> ' + window.b); } catch(e) { l(e.message); };
try { l('window.c() -> ' + window.c()); } catch(e) { l(e.message); };
try { l('window.d() -> ' + window.d()); } catch(e) { l(e.message); };
try { l('window.e() -> ' + window.e()); } catch(e) { l(e.message); };
try { l('window.x() -> ' + window.x()); } catch(e) { l(e.message); };
铬合金31,FF 26,IE 8-10:

window.a->未定义
窗口b->2
window.c()->3
window.d()->4
window.e()->5
a没有定义
IE 7的行为不符合预期:

window.a->未定义
窗口b->2
window.c()->3
对象不支持此属性或方法
预期功能
window.x()->1
IE 7和命名函数 似乎命名函数是调用
eval
的作用域的一部分:

2
1.
g为null或未定义
窗口级别的跨浏览器评估 实际上,“跨浏览器”的意思是“Chrome 31,FF 26,IE 7-10”:

用法示例:

evil(
    'var a = 1; alert(a); a;', 
    function (r) { console.log(r); }, 
    function (e) { console.log(e.message); }
);

// prints "1"

evil(
    'alert(b);',
    function (r) { console.log(r); }, 
    function (e) { console.log(e.message); }
);

// prints "b is not defined"

你认为将整个代码封装到一个函数中会使这个模式安全吗?如果它在一个函数中,并且该函数是通过一些上下文调用的,这样就定义了
this
。例如,如果函数是通过对象属性访问的,或者如果您使用
.call()
@wared调用周围的函数,那么这个规范在这一点上是非常奇怪的;这可能是有原因的,但我不知道是什么原因。我模糊地记得一篇关于这个主题或相关主题的老Kangax博客文章。
eval
只有在您已经知道字符串在解释为JavaScript代码时会做什么时才是“安全的”(就像在您的示例中,字符串是常量)。如果您确实知道,那么有一些简单的解决方案(
function(){…}
,context对象)通常运行速度快十几倍,并且没有
eval
著名的任何风险。
function evil(code, success, failure) {
    var head, script;
    script = document.createElement('script');
    script.text = [
        'try{_logevil[0](eval(\'', 
            code.replace(/('|\\)/g, '\\\$1'), 
        '\'))}catch(e){_logevil[1](e);}'
    ].join('');
    window._logevil = [success, failure];
    head = document.getElementsByTagName('head')[0];
    head.appendChild(script);
    head.removeChild(script);
}
evil(
    'var a = 1; alert(a); a;', 
    function (r) { console.log(r); }, 
    function (e) { console.log(e.message); }
);

// prints "1"

evil(
    'alert(b);',
    function (r) { console.log(r); }, 
    function (e) { console.log(e.message); }
);

// prints "b is not defined"