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

时间:2014-01-07 14:41:44

标签: 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无法访问周围的范围?换句话说,使用eval.call({}, '...')代替eval('...')eval只能访问对象的成员吗?


这里的一些测试:https://stackoverflow.com/a/20975485/1636522

2 个答案:

答案 0 :(得分:2)

如果eval没有“调用上下文” - 基本上是this的本地值 - 那么执行上下文就是全局上下文。 Section 10.4.2 of the spec.

所以这应该有效:

!function(){
    var a = 1;
    function evalWrapper() {
        var b = 2;
        try { eval('a'); console.log("yaay!"); } 
        catch (e) { console.log(e.message); }
    };
    evalWrapper.call({});
}();

规范中描述的第一个场景是:

  

如果没有调用上下文或者eval代码没有被eval函数直接调用(15.1.2.1.1)评估,那么,a。将执行上下文初始化为使用eval代码作为C的全局执行上下文,如10.4.1.1中所述。

因此,除了直接调用eval( something )之外,您所做的任何事情 - 包括,如果您阅读了引用的部分,则通过名称“eval”之外的其他内容引用“eval”函数! - 然后在全局上下文中评估字符串中的代码,包括全局词法上下文。因此,好像代码根本不在周围的功能中。

答案 1 :(得分:1)

附加测试

尝试从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); }
}();

Chrome:

1
b is not defined
c is not defined 

跨浏览器测试

尝试从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); };

Chrome 31,FF 26,IE 8-10:

window.a -> undefined
window.b -> 2
window.c() -> 3
window.d() -> 4
window.e() -> 5
a is not defined 

IE 7的行为不符合预期:

window.a -> undefined
window.b -> 2
window.c() -> 3
object doesn't support this property or method
function expected
window.x() -> 1

IE 7和命名函数

eval.call(window, 'function f(){return 1}') 
!function () {
    eval.call(window, 'function g(){return 2}');
    console.log(g());
}();
console.log(f());
console.log(g());

似乎命名函数是调用eval的范围的一部分:

2
1
g is null or undefined

窗口级别的跨浏览器评估

实际上,"跨浏览器"意味着" Chrome 31,FF 26,IE 7-10" :)

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"