2012-01-26 9 views
22

Supponiamo di avere una variabile nell'ambito globale.È possibile limitare l'ambito di una funzione javascript?

Supponiamo che io voglio definire una funzione che posso garanzia non avranno accesso a questa variabile, c'è un modo per avvolgere la funzione, o chiamare la funzione, che garantirà questo?

Infatti, ho bisogno di qualsiasi funzione prescritta per avere un accesso ben definito alle variabili e che l'accesso sia definito prima e separato dalla definizione di tale funzione.

Motivazione: Sto valutando la possibilità delle funzioni inviate dall'utente. Dovrei essere in grado di credere che la funzione sia una varietà di "sicuro" e quindi essere felice di pubblicarli sul mio sito.

+1

Date un'occhiata a http://www.adsafe.org/ –

+2

Una cosa si può certamente fare è evitare le variabili globali, in generale comunque una buona pratica. – Pointy

+0

@Pointy: questo non impedisce al codice non affidabile di accedere al DOM e di armeggiare con la tua pagina. – josh3736

risposta

17

Eseguire il codice in un iframe ospitato su un'origine diversa. Questo è l'unico modo per garantire a che il codice non attendibile è in modalità sandbox e non è possibile accedere alle informazioni globali o al DOM della pagina.

+1

Proprio come fa jsFiddle - mi aspetterei certamente che abbiano fatto delle ricerche e trovato che fosse l'opzione migliore. –

+0

Questo è ciò che caja fa; http://code.google.com/p/google-caja/ –

3

Vado a dare una risposta tecnica alla tua domanda con almeno una possibilità. Utilizzare il nome del globale come un argomento per quella funzione:

someGlobal = 5; 

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

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

sarebbe meglio naturalmente solo di non utilizzare le variabili globali mai.

+0

Non credo che maschererebbe 'console.log (window.someGlobal);' –

+2

Non funziona. 'this.someGlobal' o' window.someGlobal' o 'eval ('someGlobal')' – josh3736

3

Non è possibile limitare l'ambito di una funzione utilizzando i metodi "call" o "apply", ma è possibile utilizzare un semplice trucco utilizzando "eval" e scoping per nascondere sostanzialmente eventuali variabili globali specifiche dalla funzione da chiamato.

Il motivo è perché la funzione ha accesso alle variabili "globali" dichiarate nell'ambito che la funzione stessa ha dichiarato. Quindi, copiando il codice per il metodo e iniettandolo in eval, puoi essenzialmente modificare l'ambito globale della funzione che stai cercando di chiamare. Il risultato finale è essenzialmente essere in grado di un po 'sandbox un pezzo di codice javascript.

Ecco un esempio di codice completo:

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

Il codice che viene eval'd assomiglia a questo:

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

[Non funziona.] (http://jsfiddle.net/josh3736/JJPBt/) La funzione è ancora chiamata nell'ambito globale, quindi tutto devi fare 'this.document.title'. – josh3736

+0

@ josh3736 Facile da correggere con .call ({}) o .apply ({}) - http://jsfiddle.net/agoywobt/ –

6

Un po 'tardi, ma forse vi aiuterà un po'

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

    } 

Esempio di utilizzo

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

Questo non è infallibile. "trace ((function() {return this}()));" mostrerebbe ancora l'accesso all'ambito globale (e ci sono altri modi). –

9

Utilizzo di Web Workers integrati potrebbe consentire l'esecuzione di funzioni sicure. Qualcosa di simile consente a un utente di inserire javascript, eseguirlo e ottenere il risultato senza avere accesso al contesto globale.

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>

È possibile provare questi per esempio incollandolo nella textarea:

return "I'm a safe function"; 

si può vedere che è sicuro:

return globalVariable; 

È anche possibile avere script più complessi, qualcosa come questo:

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

Vedi informazioni webworkers qui, i lavoratori web soprattutto incorporati: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers#Embedded_workers

+0

È possibile passare il parametro alla funzione definita dall'utente? –

+0

@DulguunOtgon Puoi passare i parametri delle stringhe aggiungendoli alla funzione userDefined (parametri) {... –

3

È possibile utilizzare WebWorkers per isolare il codice:

Creare un ambiente di esecuzione completamente separata e parallela (vale a dire un thread o processo separato o un costrutto equivalente) ed eseguire il resto di questi passaggi in modo asincrono in quel contesto.

Ecco un semplice esempio:

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

Questo codice restituirà:

Uncaught ReferenceError: someGlobal is not defined

undefined

Uncaught ReferenceError: someGlobal is not defined e

Uncaught ReferenceError: window is not defined

quindi il codice è ora sicuro.

3

Creare una variabile locale con lo stesso nome. Se si dispone di una variabile globale come questo:

var globalvar; 

Nella funzione:

function noGlobal(); { 
    var globalvar; 
} 

Se la funzione si riferisce a globalvar, si riferisce a quella locale.

+0

Questo non funziona perché il codice può ancora accedere alla variabile globale chiamando 'window.globalvar'. – Jamie

+0

Hmm hai ragione, mi dispiace per quello. – Snivy

3

MODIFICA: questa risposta non nasconde le variabili window.something. Ma ha un modo pulito per eseguire il codice definito dall'utente. Sto cercando di trovare un modo per mascherare le variabili della finestra

È possibile utilizzare la funzione javascript Function.prototype.bind() per associare la funzione inviata dall'utente a una variabile di ambito personalizzata di propria scelta, in questo ambito personalizzato è possibile scegliere quali variabili condividere con la funzione definita dall'utente e quale nascondere. Per le funzioni definite dall'utente, il codice sarà in grado di accedere alle variabili che hai condiviso usando this.variableName.Ecco un esempio di elaborare l'idea:

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

L'esempio esegue le seguenti operazioni:

  1. accettare un corpo funzione dall'utente
  2. Quando l'utente fa clic su Invia, l'esempio crea un nuovo oggetto funzione dal corpo personalizzato utilizzando Function constructor. Nell'esempio viene creata una funzione personalizzata senza parametri, ma i parametri possono essere aggiunti facilmente come primo input della funzione Costruttore
  3. La funzione viene eseguita e il suo output viene stampato sullo schermo.
  4. Nei commenti è incluso un corpo di funzione di esempio che verifica quale delle variabili ha accesso alla funzione personalizzata.
0

A mia conoscenza, in Javascript, qualsiasi variabile dichiarata all'esterno di una funzione appartiene all'ambito globale ed è quindi accessibile da qualsiasi punto del codice.

Ogni funzione ha il proprio ambito e ogni variabile dichiarata all'interno di tale funzione è accessibile solo da quella funzione e da qualsiasi funzione annidata. L'ambito locale in JavaScript viene creato solo da funzioni, che vengono anche chiamate scope della funzione.

Mettere una funzione all'interno di un'altra funzione potrebbe essere una possibilità in cui si potrebbe ottenere portata ridotta (cioè ambito nidificato)

Problemi correlati