2012-01-26 14 views
22

グローバルスコープに変数があるとします。javascript関数のスコープを制限することはできますか?

の保証を定義したいとします。はこの変数にアクセスできません。関数をラップするか関数を呼び出す方法がありますか?

実際、変数へのアクセスを明確に定義する必要があり、そのアクセスはその関数定義の前に定義され、その関数定義とは別に定義する必要があります。

モチベーション: 私はユーザーが投稿した機能の可能性を考えています。私はその機能が「安全」なものであることを信じて、自分のサイトでそれらを公開することを喜ぶべきです。

+1

可能性がありますが、確かに行うことができます一つのことがあるhttp://www.adsafe.org/ –

+2

を見てみましょうとにかく一般的に良い方法であるグローバル変数を避けてください。 – Pointy

+0

@Pointy:それでも、信頼できないコードがDOMにアクセスしたり、あなたのページを改ざんするのを防ぐことはできません。 – josh3736

答えて

17

異なるOriginでホストされているiframeでコードを実行します。これは、への唯一の方法です。信頼できないコードはサンドボックス化されており、グローバルまたはページのDOMにアクセスできません。

+1

jsFiddleと同様に、私は確かにこれを研究して、それが最良の選択肢であることを見いだすでしょう。 –

+0

これはcajaの機能です。 http://code.google.com/p/google-caja/ –

3

少なくとも1つの可能性について質問に技術的な回答をします。その関数の引数としてグローバルの名前を使用してください:

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

"call"または "apply"メソッドを使用して関数のスコープを制限することはできませんが、 "eval"とスコープを使用して簡単にトリックを使用して、と呼ばれる。

理由は、関数が宣言したスコープで宣言されている "グローバル"変数に関数がアクセスしているためです。したがって、メソッドのコードをコピーしてevalに注入することで、呼び出す関数のグローバルスコープを変更することができます。最終的な結果は、本質的に、JavaScriptコードをいくらかsandboxすることができます。このようなルックスeval'd取得するコード

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

:ここ

は、完全なコード例を示します少し遅れて

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; 

、ここでは特に組み込みWeb労働者を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

を3210

Uncaught ReferenceError: window is not defined

コードは安全です。

3

同じ名前のローカル変数を作成します。あなたの関数で

var globalvar; 

:あなたはこのようなグローバル変数がある場合 は

function noGlobal(); { 
    var globalvar; 
} 

を関数がglobalvarを参照している場合、それは地元のものをいうでしょう。

+0

に追加して渡すことができます。これは、 'window.globalvar'を呼び出してコードがグローバル変数にアクセスできるため機能しません。 – Jamie

+0

あなたは正しいです、それについては残念です。 – Snivy

3

編集:この答えは、ウィンドウを隠すわけではありません。しかし、それはユーザー定義コードを実行するきれいな方法を持っています。

Function.prototype.bind()あなたが選択したカスタムスコープ変数にユーザーが提出した関数をバインドするには、javacript関数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を使用してカスタムボディから新しい関数オブジェクトを作成します。この例では、パラメータを持たないカスタム関数を作成しますが、関数コンストラクタの最初の入力としてparamsを簡単に追加できます。
  3. 関数が実行され、その出力が画面に出力されます。
  4. カスタム関数がアクセスできる変数をテストする、関数の本体のサンプルがコメントに含まれています。
0

私の知るところでは、Javascriptでは、関数の外で宣言された変数はすべてグローバルスコープに属しているため、コード内のどこからでもアクセスできます。

各関数には独自のスコープがあり、その関数内で宣言された変数にはその関数とネストされた関数からのみアクセスできます。 JavaScriptのローカルスコープは関数によってのみ作成され、関数スコープとも呼ばれます。別の関数内の関数を置く

は、あなたが減少した範囲を達成できる一つの可能​​性(つまり、ネストされたスコープ)