一个"安全的地方"来评估Javascript中的可信代码(具有特定的上下文)?[编辑:你怎么打破它?]

Kad*_*ath 7 javascript scope eval

不要在标题上评判我,我知道eval是邪恶的,但我有理由这样做,而且会非常有限.事情是这样的:我想创建一个安全的空间,我可以运行特定的(和可信的)代码,并检索结果(如果它符合我的预期).出于安全原因,我想将其从所有其他范围中删除(这个空间被要求提供结果,并且不应该自己将任何内容导出到周围的范围).

我找到了一个似乎有效的解决方案,也可以为执行添加上下文,但我不确定它是否有效,或者是否存在此系统中的安全漏洞.你能告诉我它是否有问题吗?

它实际上创建了与全局变量同名的本地变量,以防止访问它们.它还剥离了功能(我将添加功能以保留我想要保留的功能).该函数被声明为最接近全局变量,以避免可见性更高的局部范围(计划删除这些特定局部变量的参数).

function contextNoScope(root, context) {
    //I had to pass window as parameter for it to work properly
    for(var key in root){
        if(key != '0' && key != 'window'){
            eval('var ' + key + '=undefined;');
        }
    }
    var window = undefined;
    var root = undefined; //root has to be forbidden too
    return function() {
        this.eval = function(str){ return eval(str); };
        return this;
    }.call(context);
}
Run Code Online (Sandbox Code Playgroud)

用法:

(function(root){
    function contextNoScope(root, context) {
        //...same as previous
    }
    var context = { x: 3 };
    var space = contextNoScope(root, context);
    console.log(space.eval('this.x + 1'));
})(window);
Run Code Online (Sandbox Code Playgroud)

(注意:如果您以全局方式测试此代码,请不要忘记在为上下文传递全局变量时添加特殊修复,因为它将像所有其他变量一样被屏蔽.如果您需要修复此修补程序.你想尝试传递窗口作为上下文参数,原因很明显..)

编辑:

为了更清楚地了解脚本的功能以及可以解决我的问题的原因:我假设JS中没有完全安全的东西可称为"可信源",因为我们在这里谈论的是导入脚本.通过信任,我的意思是将检查脚本源,并添加安全密钥.

每个脚本从给定的上下文中获取某个变量,并为其添加属性.这里给出了名称,这意味着我们事先知道哪些var将被修改,以及添加到其中的属性的名称和类型是什么.在某种程度上,我已经可以检查是否有意做了什么,这里的问题是"确定"任何代码将无法主要干扰事件或DOM,并且不会删除或修改任何内容在给定的上下文中,除了添加propery ..

编辑2:请打破它!

(我还不是所有网站规则的专家,如果不是正确编辑的方式,请更正我:我选择不修改第一个代码并添加新版本,即使它有点长.)

我正在寻找一个适合我的解决方案的沙箱,由比我更好的人制作,但因为我很好奇并想继续学习,我在这里发布了我所做的改进版本,因为我想知道这是怎么回事可以打破.所以我现在正在寻找可以打破这个系统的精确技巧:

function contextNoScope(context) {
    var len, i, keys, val, window = root, isNaN = root.isNaN, console = root.console;
    if(!context){ context = {}; }
    keys = Object.getOwnPropertyNames(window);
    if(len = keys.length){
        for(i = 0; i < len; i++){
            val = keys[i];
            //TODO: remove 'console' from the if (debug purpose)
            if(isNaN(val) && val != 'window' && val != 'eval' && val != 'isNaN' && val != 'console'){
                eval('var ' + val + '=undefined;');
            }
        }
    }
    isNaN = undefined;
    len = undefined;
    i = undefined;
    keys = undefined;
    val = undefined;
    eval('var root = undefined');
    return function() {
        this.eval = function(str){
            if(str){
                str = str.toString();
                //TODO: replace by more relevant regex (it currently throws error for words like 'evaluation')
                if(str.indexOf('constructor') > -1 || str.indexOf('__proto__') > -1 || str.indexOf('prototype') > -1 || str.indexOf('eval') > -1){
                    console.log('ERROR - str contains forbidden code "constructor", "__proto__", "eval", "prototype"', '\n' + str);
                    return false;
                }
                try{
                    eval(str);
                    return true;
                }catch(e){
                    console.log(e + '\nin:\n' + str);
                    return false;
                }
            }
            console.log('ERROR - str is not defined: ', str);
            return false;
        };
        return this;
    }.call(context);
}
Run Code Online (Sandbox Code Playgroud)

对变化的一些解释:

  • 在第一个版本中,我添加了窗口作为参数,因为关于何时var评估单词的基本错误,没有必要.但是传递窗口作为另一个名称(这里是"root")似乎更容易.整个函数的包装方式与第一个版本的使用方式相同.
  • 添加getOwnPropertyNames以获取不可枚举的属性,但我知道向后兼容性问题,这不是我所说的"打破系统"使用旧浏览器(我已经知道)
  • 添加了函数局部参数或变量的掩码
  • isNaN(val)这里的测试不是作为一个变量来评估窗口的数字属性,它引用了i帧
  • eval('var root = undefined'); 是一个技巧,可以在行执行时创建var,而不是在最开始时创建var,就像在常规行中一样 var
  • 我知道在这个空间中声明的函数不再访问常规函数(尝试使用alert(1),它将无法工作)

小智 0

任何基于将变量设置为影子全局变量的内容都可以被 打破Function.apply(undefined),它运行一个设置this为全局上下文的函数。例如:

(function() {
    this.alert(1);
}).apply(undefined);
Run Code Online (Sandbox Code Playgroud)

任何基于正则表达式的东西都可能被以下的某种组合所破坏:

底线:你无法用 Javascript 构建一个有效的沙箱。已经尝试过了;每一次尝试都是复杂的、脆弱的,并最终被打破。