2009-02-12 5 views
29

J'ai un fichier javascript qui lit un autre fichier qui peut contenir des fragments javascript qui doivent être eval() - ed. Les fragments de script sont supposés se conformer à un sous-ensemble strict de javascript qui limite ce qu'ils peuvent faire et quelles variables ils peuvent changer, mais je veux savoir s'il y a moyen de le faire en empêchant l'eval de voir les variables dans la portée globale . Quelque chose comme ce qui suit:Restreindre eval() à une portée étroite

function safeEval(fragment) 
{ 
    var localVariable = g_Variable; 

    { 
     // do magic scoping here so that the eval fragment can see localVariable 
     // but not g_Variable or anything else outside function scope 

     eval(fragment); 
    } 
} 

Le code réel n'a pas besoin de ressembler à ceci - je suis ouvert à tout et tous les trucs bizarres avec des fermetures, etc. Mais je ne veux savoir si cela est encore possible.

+0

Je ne sais pas si cela fonctionnera pour 'eval' mais vous pouvez essayer de commutateur son contexte d'exécution (objet d'activation google). – jfs

+0

Jetez un oeil à l'implémentation de dojox.secure.sandbox. – jfs

+0

Maintenant, ce que vous voulez probablement est un travailleur Web. – bjb568

Répondre

50

Réponse courte: Non. Si c'est dans la portée globale, c'est disponible pour n'importe quoi.

Réponse longue: si vous êtes eval() ing code non fiable qui vraiment veut lire ou jouer avec votre environnement d'exécution, vous êtes foutus. Mais si vous possédez et faites confiance à tout le code en cours d'exécution, y compris qu'être eval() ed, vous pouvez simuler en remplaçant le contexte d'exécution:

function maskedEval(scr) 
{ 
    // set up an object to serve as the context for the code 
    // being evaluated. 
    var mask = {}; 
    // mask global properties 
    for (p in this) 
     mask[p] = undefined; 

    // execute script in private context 
    (new Function("with(this) { " + scr + "}")).call(mask); 
} 

Encore une fois, je dois souligner:

Cela ne servira pour boucler code approuvé à partir du contexte dans lequel il est exécuté. Si vous ne faites pas confiance au code, NE PAS le eval() (ou le passer au nouveau Function(), ou l'utiliser de toute autre manière qui se comporte comme eval()).

+0

Il y a un petit gotcha avec l'approche 'with' qui m'a mordu avant. Les fonctions déclarées dans le bloc with n'ont pas accès à la cible de l'instruction with. –

+0

Ouais, je m'attends à une fonction * declaration * qui pourrait être "hissée" dans ce cas - vous voudriez coller aux expressions. – Shog9

+0

putain .... je ne peux pas dire à quel point je suis heureux: D c'est génial !! maintenant je peux compléter mon template-engine ... je dois tester plus, mais je pense que j'ai déjà tout ... putain ... c'est assez cool man ... tu sais ... pour analyser les expressions chargées à partir du template était dur , mais avec ceci c'est assez facile: D Connaissez-vous des fuites de sécurité? Je n'ai jamais su cela "avec (ceci)" ... je sais que je dois vérifier vor keaywords, pour enlever l'injection maléfique: D – Mephiztopheles

2

Vous ne pouvez pas limiter la portée de eval

BTW voir this post

Il peut y avoir un autre moyen d'accomplir ce que vous voulez accomplir dans le grand schéma des choses, mais vous ne pouvez pas limiter la portée de eval de quelque manière que. Vous pouvez masquer certaines variables en tant que variables pseudo privées dans javascript, mais je ne pense pas que ce soit ce que vous recherchez.

2

Voici une idée. Et si vous utilisiez un analyseur statique (quelque chose que vous pourriez construire avec esprima, par exemple) pour déterminer quelles variables externes le code eval'd utilise, et les alias. Par "code extérieur" je veux dire les variables le code eval'd utilise mais ne déclare.Voici un exemple:

eval(safeEval(
    "var x = window.theX;" 
    +"y = Math.random();" 
    +"eval('window.z = 500;');")) 

où safeEval retourne la chaîne javascript modifié avec un contexte qui bloque l'accès à des variables extérieures:

";(function(y, Math, window) {" 
    +"var x = window.theX;" 
    +"y = Math.random();" 
    +"eval(safeEval('window.z = 500;');" 
"})();" 

Il y a deux choses que vous pouvez faire maintenant avec cette:

  • Vous pouvez vous assurer que le code eval'd ne peut pas lire les valeurs des variables externes, ni y écrire (en passant undefined comme arguments de la fonction, ou ne pas passer d'arguments). Ou vous pouvez simplement lancer une exception dans les cas où les variables sont inaccessibles.
  • Vous aussi faire en sorte que les variables créées par eval ne modifient pas la portée autour
  • Vous pouvez permettre à eval de créer des variables dans le champ environnant en déclarant ces variables en dehors de la fermeture plutôt que comme paramètres de la fonction
  • Vous pouvez permettre accès en lecture seule par copie valeurs des variables externes et les utiliser comme arguments à la fonction
  • Vous pouvez autoriser l'accès en lecture-écriture à des variables spécifiques en disant safeEval de ne pas créer un alias ces noms particuliers
  • Vous pouvez détecter les cas où l'eval fait not modifier une variable particulière et lui permettre d'être automatiquement exclu de l'alias (par exemple. Math dans ce cas, n'est pas modifiée)
  • Vous pouvez donner le eval un contexte dans lequel exécuter, en passant dans les valeurs des arguments qui peuvent être différents que le contexte entourant
  • Vous pourriez saisir les changements de contexte par aussi renvoyer les arguments de la fonction pour que vous puissiez les examiner en dehors de l'eval.

Notez que l'utilisation de eval est un cas particulier, puisque par sa nature, il ne peut effectivement être enveloppé dans une autre fonction (ce qui est la raison pour laquelle nous devons faire eval(safeEval(...))).

Bien sûr, faire tout ce travail peut ralentir votre code, mais il y a certainement des endroits où le coup n'aura pas d'importance. J'espère que cela aide quelqu'un. Et si quelqu'un crée une preuve de concept, j'aimerais voir un lien ici;

1

N'utilisez pas eval. Il existe une alternative, js.js: JS interpreter written in JS, afin que vous puissiez exécuter des programmes JS dans n'importe quel environnement que vous avez réussi à configurer. Voici un exemple de son API de la page du projet:

var jsObjs = JSJS.Init(); 
var rval = JSJS.EvaluateScript(jsObjs.cx, jsObjs.glob, "1 + 1"); 
var d = JSJS.ValueToNumber(jsObjs.cx, rval); 
window.alert(d); // 2 
JSJS.End(jsObjs); 

Rien d'effrayant, comme vous pouvez le voir.

+0

3 Mo et seulement 594 Ko après compression gzip – prototype

+0

@ user645715 1) Essayez d'utiliser Closure Compiler pour le compresser encore plus. 2) Il y a un ralentissement de 200x, ce qui est bien pire que le code source de 3 Mo. 3) Il n'y a pas d'autre solution pour le moment. –

1

Similaire au script de retour de fonction dynamique dans une approche de bloc with ci-dessus, cela vous permet d'ajouter des pseudo-globaux au code que vous voulez exécuter. Vous pouvez "cacher" des choses spécifiques en les ajoutant au contexte.

function evalInContext(source, context) { 
    source = '(function(' + Object.keys(context).join(', ') + ') {' + source + '})'; 

    var compiled = eval(source); 

    return compiled.apply(context, values()); 

    // you likely don't need this - use underscore, jQuery, etc 
    function values() { 
     var result = []; 
     for (var property in context) 
      if (context.hasOwnProperty(property)) 
       result.push(context[property]); 
     return result; 
    } 
} 

Voir pour un exemple. Notez que Object.keys n'est pas pris en charge dans tous les navigateurs.

0

N'exécutez pas de code en qui vous n'avez pas confiance. Les globals seront toujours accessibles. Si vous faites confiance au code, vous pouvez l'exécuter avec des variables particulières dans son champ d'application comme suit:

(new Function("a", "b", "alert(a + b);"))(1, 2); 

cela équivaut à:

(function (a, b) { 
    alert(a + b); 
})(1, 2); 
0

Shog9 ♦ de réponse est grande. Mais si votre code n'est qu'une expression, le code sera exécuté et rien ne sera retourné. Pour les expressions, utilisez

function evalInContext(context, js) { 

    return eval('with(context) { ' + js + ' }'); 

} 

Voici comment l'utiliser:

var obj = {key: true}; 

evalInContext(obj, 'key ? "YES" : "NO"'); 

Il retournera "YES".

Si vous n'êtes pas sûr si le code à exécuter est des expressions ou des déclarations, vous pouvez les combiner:

function evalInContext(context, js) { 

    var value; 

    try { 
    // for expressions 
    value = eval('with(context) { ' + js + ' }'); 
    } catch (e) { 
    if (e instanceof SyntaxError) { 
     try { 
     // for statements 
     value = (new Function('with(this) { ' + js + ' }')).call(context); 
     } catch (e) {} 
    } 
    } 

    return value; 
} 
Questions connexes