2012-01-26 116 views
22

假设我在全局范围内有一个变量。是否可以限制javascript函数的范围?

假设我想定义一个函数,我可以保证将无法​​访问这个变量,有没有一种方法来包装函数,或调用函数,这将确保这?

事实上,我需要任何预定功能具有明确定义的访问的变量,并且该接入被之前定义,并且从该函数定义独立。

动机: 我考虑的用户提交的功能的可能性。我应该能够相信这个功能是一些“安全”的功能,因此很高兴在我自己的网站上发布它们。

+1

看看http://www.adsafe.org/ –

+2

有一件事你肯定可以做的是避免使用全局变量,无论如何这是一个很好的做法 – Pointy

+0

@Pointy:这仍然不能防止不可信的代码访问DOM并修改你的页面。 – josh3736

回答

17

运行在托管在不同产地的iframe的代码。这是保证不可信代码被沙箱化并阻止访问全局变量或页面的DOM的唯一方法。

+1

就像jsFiddle一样 - 我一定会期待他们对此进行研究并发现它是最好的选择。 –

+0

这就是caja所做的; http://code.google.com/p/google-caja/ –

3

我打算至少给出一种可能性的技术答案。使用全球的名称作为参数传递给该函数:

someGlobal = 5; 

function cantSeeThatGlobal(someGlobal) { 
    console.log(someGlobal); 
} 

cantSeeThatGlobal(); // prints undefined 
cantSeeThatGlobal(10); // prints 10 

这将是当然的只是为了更好地不使用全局变量如初。

+0

我不相信会屏蔽'console.log(window.someGlobal);' –

+2

不起作用。 'this.someGlobal'或'window.someGlobal'或'eval('someGlobal')' – josh3736

3

您不能限制使用“呼叫”或“应用”方法功能的范围,但可以使用“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> 

是被eval'd看起来像这样的代码:后期

var window = {}; 
var document = {}; 
(function displayTitle() 
{ 
    alert(document.title); 
})(); 
+0

[不起作用。](http://jsfiddle.net/josh3736/JJPBt/)该函数仍在全局范围内调用,所以全部你必须做'this.document.title'。 – josh3736

+0

@ josh3736易于使用.call({})或.apply({})修复 - http://jsfiddle.net/agoywobt/ –

6

有一点,但也许它会帮助你有点

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); 

    } 

使用示例

// dummy to test if we can access the DOM 
var dummy = function() { 

    this.notify = function(msg) { 
     console.log(msg); 
     }; 

    } 

var result = RestrictFunction({ 

    // Custom data to pass to the user script , Accessible via `this` 
    data:{ 
     prop1: 'hello world', 
     prop2: ["hello","world"], 
     prop3: new dummy() 
     }, 

    // User custom script as string or function 
    script:function() { 

     trace(this); 

     this.msg = "hello world"; 
     this.prop3.notify(this.msg); 

     setTimeout(function() { 
      trace(this); 
      } , 10); 

     trace(data); 
     trace(params); 
     trace(scope); 
     trace(window); 
     trace(XMLHttpRequest); 
     trace(eval); 

     return "done!"; // not required to return value... 

     }, 

    }); 

console.log("result:" , result); 
+0

这并非万无一失。 “trace((function(){return this()));”仍然可以访问全球范围(还有其他方式)。 –

9

使用嵌入式Web工人可能允许运行安全功能。像这样的东西允许用户输入JavaScript,运行它并获得结果,而无需访问您的全局上下文。

globalVariable = "I'm global"; 
 

 
document.getElementById('submit').onclick = function() { 
 
    createWorker(); 
 
} 
 

 

 
function createWorker() { 
 
    // The text in the textarea is the function you want to run 
 
    var fnText = document.getElementById('fnText').value; 
 

 
    // You wrap the function to add a postMessage 
 
    // with the function result 
 
    var workerTemplate = "\ 
 
function userDefined(){" + fnText + 
 
    "}\ 
 
postMessage(userDefined());\ 
 
onmessage = function(e){console.log(e);\ 
 
}" 
 

 
    // web workers are normally js files, but using blobs 
 
    // you can create them with strings. 
 
    var blob = new Blob([workerTemplate], { 
 
    type: "text/javascript" 
 
    }); 
 

 
    var wk = new Worker(window.URL.createObjectURL(blob)); 
 
    wk.onmessage = function(e) { 
 
    // you listen for the return. 
 
    console.log('Function result:', e.data); 
 
    } 
 

 
}
<div>Enter a javascript function and click submit</div> 
 
<textarea id="fnText"></textarea> 
 
<button id="submit"> 
 
    Run the function 
 
</button>

您可以在文本区域粘贴尝试这些例如:

return "I'm a safe function"; 

你可以看到,它的安全:

return globalVariable; 

你甚至可以有更复杂的脚本,如下所示:

var a = 4, b = 5; 
function insideFn(){ 
    // here c is global, but only in the worker context 
    c = a + b; 
} 
insideFn(); 
return c; 

查看关于webworkers的信息在这里,特别是嵌入式网络工作者: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers#Embedded_workers

+0

是否可以将参数传递给用户定义的函数? –

+0

@DulguunOtgon您可以通过将函数添加到函数userDefined(parameters)来传递字符串参数{... –

3

您可以使用WebWorkers隔离代码:

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

下面是一个简单的例子:

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)'); 

该代码将返回:

Uncaught ReferenceError: someGlobal is not defined

undefined

Uncaught ReferenceError: someGlobal is not defined

Uncaught ReferenceError: window is not defined

让你的代码现在是安全的。

3

创建一个具有相同名称的本地变量。 如果你有这样一个全局变量:

var globalvar; 

在你的函数:

function noGlobal(); { 
    var globalvar; 
} 

如果该功能是指globalvar,它将是指当地的一个。

+0

这不起作用,因为代码仍然可以通过调用'window.globalvar'访问全局变量。 – Jamie

+0

嗯,你是对的,对不起。 – Snivy

3

编辑:这个答案不隐藏window.something变量。但它有一个干净的方式来运行用户定义的代码。我试图找到一种方法来掩盖窗口变量

您可以使用JavaScript函数Function.prototype.bind()提交功能,你所选择的自定义范围变量的用户绑定,在这个自定义的范围,你可以选择共享哪些变量与用户定义的功能,以及隐藏。对于用户定义的函数,代码将能够访问使用this.variableName共享的变量。下面是详细阐述理念的例子:

// A couple of global variable that we will use to test the idea 
 
var sharedGlobal = "I am shared"; 
 
var notSharedGlobal = "But I will not be shared"; 
 

 
function submit() { 
 
    // Another two function scoped variables that we will also use to test 
 
    var sharedFuncScope = "I am in function scope and shared"; 
 
    var notSharedFuncScope = "I am in function scope but I am not shared"; 
 

 
    // The custom scope object, in here you can choose which variables to share with the custom function 
 
    var funcScope = { 
 
    sharedGlobal: sharedGlobal, 
 
    sharedFuncScope: sharedFuncScope 
 
    }; 
 

 
    // Read the custom function body 
 
    var customFnText = document.getElementById("customfn").value; 
 
    // create a new function object using the Function constructor, and bind it to our custom-made scope object 
 
    var func = new Function(customFnText).bind(funcScope); 
 

 
    // execute the function, and print the output to the page. 
 
    document.getElementById("output").innerHTML = JSON.stringify(func()); 
 

 
} 
 

 
// sample test function body, this will test which of the shared variables does the custom function has access to. 
 
/* 
 
return { 
 
     sharedGlobal : this.sharedGlobal || null, 
 
     sharedFuncScope : this.sharedFuncScope || null, 
 
     notSharedGlobal : this.notSharedGlobal || null, 
 
     notSharedFuncScope : this.notSharedFuncScope || null 
 
}; 
 
*/
<script type="text/javascript" src="app.js"></script> 
 
<h1>Add your custom body here</h1> 
 
<textarea id="customfn"></textarea> 
 
<br> 
 
<button onclick="submit()">Submit</button> 
 
<br> 
 
<div id="output"></div>

该示例执行以下操作:

  1. 从用户
  2. 当用户接受一个函数体点击提交,示例使用Function constructor从定制主体创建一个新的函数对象。在这个例子中,我们创建了一个没有参数的自定义函数,但是可以轻松添加参数作为函数构造函数的第一个输入。
  3. 该函数被执行,并且其输出被打印在屏幕上。
  4. 注释中包含示例函数体,用于测试自定义函数可以访问哪些变量。
0

据我所知,在Javascript中,任何在函数外声明的变量都属于全局作用域,因此可以从代码中的任何位置访问。

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

把功能的其他功能里面可能是一个可能性,在那里你可以达到降低范围(即嵌套的范围)

相关问题