是否可以限制javascript函数的范围?

是否可以限制javascript函数的范围?,javascript,security,Javascript,Security,假设我在全局范围内有一个变量 假设我希望定义一个我可以保证不会访问此变量的函数,有没有一种方法可以包装该函数或调用该函数,从而确保这一点 事实上,我需要任何规定的函数对变量具有定义良好的访问权限,并且该访问权限必须在函数定义之前定义,并且与函数定义分离 动机: 我正在考虑用户提交函数的可能性。我应该能够相信该功能是某种“安全”的,因此我很乐意在我自己的网站上发布它们。在托管在不同来源的iframe中运行代码。这是保证不受信任的代码被沙盒化并阻止访问全局或页面DOM的唯一方法。您不能使用“调用”或

假设我在全局范围内有一个变量

假设我希望定义一个我可以保证不会访问此变量的函数,有没有一种方法可以包装该函数或调用该函数,从而确保这一点

事实上,我需要任何规定的函数对变量具有定义良好的访问权限,并且该访问权限必须在函数定义之前定义,并且与函数定义分离

动机:
我正在考虑用户提交函数的可能性。我应该能够相信该功能是某种“安全”的,因此我很乐意在我自己的网站上发布它们。

在托管在不同来源的
iframe
中运行代码。这是保证不受信任的代码被沙盒化并阻止访问全局或页面DOM的唯一方法。

您不能使用“调用”或“应用”方法限制函数的范围,但可以使用“eval”使用简单的技巧和作用域,从本质上对要调用的函数隐藏任何特定的全局变量

这是因为函数可以访问在函数本身声明的范围内声明的“全局”变量。因此,通过复制该方法的代码并将其注入eval,您可以从本质上更改要调用的函数的全局范围。最终的结果是基本上能够对一段javascript代码进行沙箱处理

下面是一个完整的代码示例:

<html>
<head>
<title>This is the page title.</title>
<script>
    function displayTitle()
    {
        alert(document.title);
    }

    function callMethod(method)
    {
        var code = "" +
            // replace global "window" in the scope of the eval
            "var window = {};" +
            // replace global "document" in the scope of the eval
            "var document = {}; " +
            "(" +

            // inject the Function you want to call into the eval
                method.toString() +

            // call the injected method
            ")();" +
            "";
        eval(code);
    }

    callMethod(displayTitle);
</script>
</head>
<body></body>
</html>
someGlobal = 5;

//As a worker normally take another JavaScript file to execute we convert the function in an URL: http://stackoverflow.com/a/16799132/2576706
function getScriptPath(foo) {
  return window.URL.createObjectURL(new Blob([foo], {
    type: 'text/javascript'
  }));
}

function protectCode(code) {
  var worker = new Worker(getScriptPath(code));
}

protectCode('console.log(someGlobal)'); // prints 10
protectCode('console.log(this.someGlobal)');
protectCode('console.log(eval("someGlobal"))');
protectCode('console.log(window.someGlobal)');

有点晚了,但也许对你有点帮助

function RestrictFunction(params) {

    params = ( params == undefined ? {} : params );
    var scope = ( params.scope == undefined ? window : params.scope );
    var data = ( params.data == undefined ? {} : params.data );
    var script = ( params.script == undefined ? '' : params.script );
    if (typeof params.script == 'function') {
        script = params.script.toString();
        script = script.substring(script.indexOf("{") + 1, script.lastIndexOf("}"));
        }

    // example: override native functions that on the white list

    var setTimeout = function(_function,_interval) {

        // this is important to prevent the user using `this` in the function and access the DOM
        var interval = scope.setTimeout( function() { 
            RestrictFunction({
                scope:scope,
                data:data,
                script:_function
                });
            } , _interval );

        // Auto clear long user intervals
        scope.setTimeout( function() {
            scope.clearTimeout(interval);
            } , 60*1000 );

        return interval;
        }       

    // example: create custom functions

    var trace = function(str) {
        scope.console.log(str);
        }   

    return (function() {

        // remove functions, objects and variables from scope

        var queue = [];
        var WhiteList = [
            "Blob","Boolean","Date","String","Number","Object","Array","Text","Function",
            "unescape","escape","encodeURI","encodeURIComponent","parseFloat","parseInt",
            "isNaN","isFinite","undefined","NaN",
            "JSON","Math","RegExp",
            "clearTimeout","setTimeout"
            ];

        var properties = Object.getOwnPropertyNames(scope);
        for (var k = 0; k<properties.length; k++ ) {
            if (WhiteList.indexOf(properties[k])!=-1) continue;
            queue.push("var "+properties[k]+" = undefined;");
            }   

        for (var k in scope) {
            if (WhiteList.indexOf(k)!=-1) continue;
            queue.push("var "+k+" = undefined;");
            }

        queue.push("var WhiteList = undefined;");   
        queue.push("var params = undefined;")   ;
        queue.push("var scope = undefined;")    ;
        queue.push("var data = undefined;") ;
        queue.push("var k = undefined;");   
        queue.push("var properties = undefined;");  
        queue.push("var queue = undefined;");   
        queue.push("var script = undefined;");  
        queue.push(script); 

        try {
        return eval( '(function(){'+ queue.join("\n") +'}).apply(data);' ); 
        } catch(err) { }

        }).apply(data);

    }   

使用嵌入式Web Workers可以运行安全功能。类似的东西允许用户输入javascript,运行它并获得结果,而无需访问全局上下文

globalVariable=“我是全球的”;
document.getElementById('submit')。onclick=function(){
createWorker();
}
函数createWorker(){
//textarea中的文本是要运行的函数
var fnText=document.getElementById('fnText')。值;
//包装函数以添加postMessage
//与函数结果一致
var workerTemplate=”\
函数userDefined(){“+fnText+
"}\
postMessage(userDefined())\
onmessage=function(e){console.log(e)\
}"
//web Worker通常是js文件,但使用Blob
//可以使用字符串创建它们。
var blob=新blob([workerTemplate]{
键入:“text/javascript”
});
var wk=newworker(window.URL.createObjectURL(blob));
wk.onmessage=函数(e){
//你听着,等待回报。
console.log('函数结果:',e.data);
}
}
输入一个javascript函数,然后单击提交
运行函数
您可以使用隔离代码:

创建一个完全独立的并行执行环境(即单独的线程、进程或等效构造),并在该上下文中异步运行这些步骤的其余部分

下面是一个简单的例子:

<html>
<head>
<title>This is the page title.</title>
<script>
    function displayTitle()
    {
        alert(document.title);
    }

    function callMethod(method)
    {
        var code = "" +
            // replace global "window" in the scope of the eval
            "var window = {};" +
            // replace global "document" in the scope of the eval
            "var document = {}; " +
            "(" +

            // inject the Function you want to call into the eval
                method.toString() +

            // call the injected method
            ")();" +
            "";
        eval(code);
    }

    callMethod(displayTitle);
</script>
</head>
<body></body>
</html>
someGlobal = 5;

//As a worker normally take another JavaScript file to execute we convert the function in an URL: http://stackoverflow.com/a/16799132/2576706
function getScriptPath(foo) {
  return window.URL.createObjectURL(new Blob([foo], {
    type: 'text/javascript'
  }));
}

function protectCode(code) {
  var worker = new Worker(getScriptPath(code));
}

protectCode('console.log(someGlobal)'); // prints 10
protectCode('console.log(this.someGlobal)');
protectCode('console.log(eval("someGlobal"))');
protectCode('console.log(window.someGlobal)');
此代码将返回:

未捕获引用错误:未定义someGlobal

未定义

未捕获引用错误:未定义someGlobal

未捕获引用错误:未定义窗口


因此,您的代码现在是安全的。

创建具有相同名称的局部变量。 如果有如下全局变量:

var window = {};
var document = {};
(function displayTitle()
{
    alert(document.title);
})();
var globalvar;
在你的职能中:

function noGlobal(); {
    var globalvar;
}

如果函数引用globalvar,则它将引用本地函数。

编辑:此答案不会隐藏窗口变量。但它有一种干净的方式来运行用户定义的代码。我试图找到一种方法来屏蔽窗口变量

您可以使用javascript函数将用户提交的函数绑定到您选择的自定义范围变量,在此自定义范围中,您可以选择与用户定义的函数共享哪些变量,以及隐藏哪些变量。对于用户定义的函数,代码将能够访问您使用this.variableName共享的变量。下面是一个详细阐述该想法的示例:

//我们将使用几个全局变量来测试这个想法
var sharedGlobal=“我是共享的”;
var notSharedGlobal=“但我不会被共享”;
函数提交(){
//我们还将使用另外两个函数作用域变量来测试
var sharedFuncScope=“我在功能范围内并已共享”;
var notSharedFuncScope=“我在函数范围内,但我没有被共享”;
//自定义范围对象,在这里您可以选择与自定义函数共享哪些变量
var funcScope={
sharedGlobal:sharedGlobal,
sharedFuncScope:sharedFuncScope
};
//阅读自定义函数体
var customFnText=document.getElementById(“customfn”).value;
//使用函数构造函数创建一个新的函数对象,并将其绑定到定制的范围对象
var func=新函数(customFnText).bind(funcScope);
//执行该函数,并将输出打印到页面。
document.getElementById(“输出”).innerHTML=JSON.stringify(func());
}
//示例测试函数体,这将测试自定义函数可以访问哪些共享变量。
/* 
返回{
sharedGlobal:this.sharedGlobal | | null,
sharedFuncScope:this.sharedFuncScope | | null,
notSharedGlobal:this.notSharedGlobal | | null,
notSharedFuncScope:this.notSharedFuncScope | | null
}; 
*/

在此处添加自定义实体

提交

据我所知,在Javascript中,函数之外声明的任何变量都属于全局范围,因此可以从代码中的任何地方访问

每个函数都有自己的作用域,在该函数中声明的任何变量都只能从该函数和任何嵌套函数中访问。JavaScript中的局部作用域仅由函数创建,也称为函数作用域

将一个函数放在另一个函数中可能是
<h1>
child
</h1>

<script>
abc = 'child';

function foo() {
  console.log('child foo: abc = ', abc);
}

console.log('-calling from child-');
parent.foo();
</script>
-calling from child-
parent foo: abc = parent
-calling from parent-
child foo: abc = child
<h1>parent</h1>

<iframe></iframe>

<script>
const iframe = document.querySelector('iframe');
iframe.addEventListener('load', function() {
  console.log('-call from parent-');
  const fn = iframe.contentWindow.makeFn(`(
      function() {
        return abc;
      }
  )`);
  console.log('from fn:', fn());
});
iframe.src = 'child.html';

</script>
<h1>
child
</h1>

<script>
abc = 'child';

function makeFn(s) {
  return eval(s);
}
</script>
-call from parent-
from fn: child
function loadIFrame(src) {
  return new Promise((resolve) => {
    const iframe = document.createElement('iframe');
    iframe.addEventListener('load', resolve);
    iframe.src = src;
    iframe.style.display = 'none';
    document.body.appendChild(iframe);  // iframes don't load if not in the document?!?! 
  });
}