2012-01-26 33 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中的本地範圍僅由函數創建,也稱爲函數範圍。

把功能的其他功能裏面可能是一個可能性,在那裏你可以達到降低範圍(即嵌套的範圍)

相關問題