Qu'est-ce qui constitue une «mauvaise utilisation» de la fonction javascript Eval? [fermé]

13

Eval est une fonctionnalité linguistique notoirement controversée. Douglas Crockford le rejette catégoriquement. Je me demande quels risques spécifiques entraîne Eval. Selon cette question, Improper use of eval opens up your code for injection attacks.

Quelles sont les utilisations incorrectes de la commande Eval et quelles failles de sécurité ouvrent-elles?

Derek Adair
la source

Réponses:

31

À l'époque où j'implémentais le moteur JScript, je préconisais d' imprimer les chemises EVAL IS EVIL , mais malheureusement, nous n'y sommes jamais parvenus.

Mon plus gros problème avec eval n'est pas l'attaque évidente par injection de code malveillant, bien que cela soit certainement extrêmement préoccupant. Mon plus gros problème est que les gens ont tendance à l'utiliser comme un très gros marteau pour résoudre de très petits problèmes. La plupart des utilisations réelles de «eval» dans la nature lorsque je faisais partie de l'équipe JScript auraient pu être résolues de manière triviale en utilisant une table de recherche; et comme chaque objet dans JScript est déjà une table de recherche, ce n'est pas comme si c'était une lourde charge. Eval redémarre le compilateur et détruit complètement la capacité du compilateur à optimiser votre code .

Pour plus de réflexions dans ce sens, voir mes articles de 2003 sur le sujet:

Malfaisance générale:

http://blogs.msdn.com/b/ericlippert/archive/2003/11/01/53329.aspx

Injection attaque le mal:

http://blogs.msdn.com/b/ericlippert/archive/2003/11/04/53335.aspx

Eric Lippert
la source
Que evalpensez-vous de l'utilisation de gros morceaux de code comme le font des applications comme JSPacker? JSPacker + gzip entraîne généralement des tailles de fichier plus petites que les deux solutions en soi, mais comme vous le faites remarquer à juste titre, il lance essentiellement un compilateur deux fois sur le même morceau de code, plus une surcharge pour effectuer des remplacements de chaîne.
Matthew Scharley
2
@Matthew: Il y a souvent un compromis entre l'espace et le temps, et profiter de ce compromis peut parfois être une grande victoire. Si le but de la technique est d'améliorer les performances, mon avis est que si une mesure minutieuse montre que c'est une victoire significative dans des scénarios réalistes sans introduire de failles de sécurité, tant mieux, faites-le. Mais je ne connais pas assez la technique spécifique pour critiquer ses détails.
Eric Lippert
Je voudrais une des réponses que je l' avais vu soit ici ou sur le SO lui - même aurait déclaré ce que votre second états de lien: qui eval()est pas un risque de sécurité sur le code client (navigateur).
sq33G
4

La plupart des trous de sécurité sont le même type de trous qu'avec l'injection SQL, à savoir la concaténation des entrées utilisateur dans le code JavaScript. La différence étant que bien qu'il existe des moyens de garantir que cela ne se produise pas avec SQL, il n'y a pas grand-chose que vous puissiez faire avec JavaScript.

À titre d'exemple trivial et inutile, une calculatrice JavaScript simpliste:

textbox1.value = eval(textbox2.value);

Quelques exemples d'utilisation corrects sont certains des compresseurs JavaScript qui compressent JavaScript en extrayant les mots courants et en les remplaçant par de courts remplacements de 1 à 2 caractères. L'emballeur sort ensuite tout cela avec un code de remplacement de chaîne basé sur le dictionnaire généré puis évalue le résultat.

Matthew Scharley
la source
1

Il y a des choses qui sont impossibles à faire dans JS sans fonction comme eval ( eval, Functionet peut - être plus).

Prenez applypar exemple. Il est facile à utiliser lors de son utilisation sur des appels de fonction ordinaires:

foo.apply(null, [a, b, c])

Mais comment feriez-vous cela pour les objets que vous créez via une nouvelle syntaxe?

new Foo.apply(null, [a, b, c]) ne fonctionne pas, pas plus que les formulaires similaires.

Mais vous pouvez contourner cette limitation via evalou Function(j'utilise Functiondans cet exemple):

Function.prototype.New = (function () {
    var fs = [];
    return function () {
        var f = fs[arguments.length];
        if (f) {
            return f.apply(this, arguments);
        }
        var argStrs = [];
        for (var i = 0; i < arguments.length; ++i) {
            argStrs.push("a[" + i + "]");
        }
        f = new Function("var a=arguments;return new this(" + argStrs.join() + ");");
        if (arguments.length < 100) {
            fs[arguments.length] = f;
        }
        return f.apply(this, arguments);
    };
}) ();

Exemple:

Foo.New.apply(null, [a, b, c]);

Bien sûr, vous pouvez créer manuellement les fonctions qui Function.prototype.Newutilisent, mais non seulement c'est verbeux et maladroit, mais (par définition) il doit être fini. Functionpermet au code de fonctionner pour n'importe quel nombre d'arguments.

Thomas Eding
la source
2
Certes, mais la question du PO était " Quelles sont les utilisations incorrectes de la commande Eval, et quels trous de sécurité ouvrent-ils? ", Eteval() non " Quelle est la bonne utilisation de ? "
Ross Patterson
1
@ RossPatterson: Je suppose que j'ai surtout regardé le titre de la question haha.
Thomas Eding
Pourtant, +1 pour trouver un bon usage pour une mauvaise langue :-)
Ross Patterson
1

Une réponse quelque peu directe de ma part a été de développer une zone de test API orientée développeur. Nous avons donné une zone de texte sur une page et un bouton Exécuter. Le point central de la page était pour eux de pouvoir essayer des choses en Javascript contre notre API communicante iframe qui n'était pas si facile à faire dans un environnement local.

Tout ce qui aurait pu être malveillant dans ce cas aurait également pu être fait par le développeur ouvrant ses outils F12.

Katana314
la source
0

Je suis d'accord qu'il devrait très rarement être utilisé, mais j'ai trouvé un cas d'utilisation puissant pour eval.

Il y a une nouvelle fonctionnalité expérimentale dans Firefox appelée asm.js avec laquelle je travaille. Il permet de compiler un sous-ensemble limité du langage Javascript en code natif. Oui, c'est très génial, mais cela a des limites. Vous pouvez considérer le sous-ensemble limité de Javascript comme un langage de type C intégré à Javascript. Il n'est pas vraiment destiné à être lu ou écrit par des humains.

Ce sous-ensemble limité de Javascript ne me permet pas d'injecter mon code généré lors de l'exécution dans le code compilé une fois qu'il a été compilé.

J'ai écrit du code qui permet à un utilisateur d'écrire, en notation familière, une expression mathématique, et de la convertir à la volée en code asm.js. À moins que je veuille faire traiter le code côté serveur (ce que je ne fais pas), evalc'est le seul outil qui me permet de faire traiter le code résultant par le navigateur en temps réel.

Biscuits à la farine de riz
la source
0

Comme l'ont souligné d'autres, la chose la plus importante lorsque vous travaillez avec evalest de la garder en sécurité. Pour cela, vous voulez faire une vérification approfondie des arguments et garder evalle code ed simple car il est généralement beaucoup plus difficile de maintenir et de sécuriser le code généré au moment de l'exécution.

Cela étant dit, j'aime utiliser evalpour deux types de choses (même s'il existe probablement de meilleures alternatives, moins diaboliques):

  1. Puisqu'il evals'agit d'un moyen de "mettre explicitement en cache le code", il peut être utilisé pour améliorer les performances. Notez que les optimiseurs sont toujours améliorés, mais il n'y a tout simplement aucune garantie quant à ce qu'ils peuvent faire pour vous. En rendant les choses explicites dans le code, vous pouvez réellement aider l'optimiseur à prendre des décisions plus intelligentes.
  2. Il peut également être utilisé pour fournir des formes de base de sécurité de type, ainsi que d'autres fonctionnalités de langage qui font défaut à JS, sans détériorer les performances.

Cette approche d'itérateur d'objet précompilé , par exemple, montre des avantages de performance clairs lors de l'utilisation evalpour l'itération de propriété d'objet. Il montre également les débuts d'un système de type puissant qui peut fournir une vérification implicite des contraintes et bien plus encore, à peu ou pas de frais.

De nombreux développeurs Javascript chevronnés remarqueraient probablement qu'il s'agit d'un trou de lapin, car, si vous commencez à écrire Javascript de cette façon, vous changez essentiellement la façon dont vous utilisez le langage. Cependant, cela pourrait ne pas être nécessairement une mauvaise chose pour ceux qui aiment Javascript, mais il manque également des fonctionnalités de langage fondamentales qui ne peuvent être obtenues qu'en changeant la façon dont nous utilisons le langage lui-même.

Domi
la source
-3

J'ai une situation où eval ressemble à la voie à suivre, évaluant une chaîne et renvoyant une variable existante dont le nom est le même que la chaîne.

J'ai plusieurs tables: table1ResultsTable, table2ResultsTable, tableNResultsTable. J'ai des variables configurées avec les mêmes noms, qui sont des objets datables jQuery. Je les utilise pour configurer les tables et appeler des fonctions datatable jQuery dessus.

Chaque table a assigné class = "resultsTable". Maintenant, j'ai besoin d'obtenir la variable lorsque l'on clique sur la table. Je fais ça:

$('resultsTable).on('click', 'td', function(event) {
    var cResultsTable = eval(event.target.parentElement.parentElement.parentElement.id);
    [etc]
});

Je reçois donc l'id de la table dans laquelle la cellule a été cliquée, qui a le même nom que la variable d'objet datatable qui lui correspond. Si quelqu'un a une suggestion d'amélioration, j'aimerais en savoir plus.

BobRodes
la source
1
Pourquoi avez-vous besoin d'eval? L'ID doit être une chaîne simple, pas du code. Quoi qu'il en soit, event.target.parentElement.parentElement.parentElementc'est horrible. De plus, je m'attendrais à ce que l'appel eval génère une erreur "erreur de référence: [id] n'est pas défini", à moins que vous n'utilisiez délibérément des identifiants évaluables (qui sont cassés, erronés et qui pourraient provoquer des choses étranges si vous finissez générer des identifiants en double).
Brian
Peut-être que je ne me fais pas comprendre. L'ID EST une chaîne simple. C'est l'identifiant de la table qui a été cliqué. J'ai également une variable objet (définie avec la méthode jQuery datatable (), référençant la même table) avec le même nom que l'id. J'essaie d'obtenir cette variable, d'accéder à ses fonctions (en fait, d'ajouter la classe "row_selected" à la ligne sélectionnée), lorsque la table est cliquée. Depuis que j'ai écrit ceci, j'ai trouvé une amélioration, cependant. Je viens de mettre toutes les références d'objet dans un tableau, de nommer les éléments de la même manière que l'id et de brancher la chaîne d'id dans celle-ci pour obtenir la référence de l'objet.
BobRodes
Brian, si vous avez un meilleur moyen de trouver l'identifiant de la table sur laquelle vous avez cliqué, je suis à votre écoute. Pour la fonction des tables de données, je dois gérer l'événement click contre la cellule et ajouter la classe row_selected à son parentNode. Pourquoi je ne peux pas simplement gérer l'événement de ligne et y ajouter directement la classe, je ne sais pas, mais la ligne ne s'affiche pas comme sélectionnée lorsque je le fais.
BobRodes
1
Peut-être que je ne me fais pas comprendre. Si vous appelez eval sur une chaîne simple (c'est-à-dire non JS), vous lèverez une exception. Donc, votre utilisation d'eval n'a pas de sens. S'il vous arrive de générer une variable d'objet avec le même nom que l'id, votre code fonctionnera ... mais me semble cassé. Je préfère créer la variable en utilisant document.getElementById(ID).MySpecialProperty = MYSPECIALPROPERTYVALUE(pour ne pas dire que c'est super non plus, mais c'est mieux que eval). Comme le souligne Eric Lippert, "chaque objet dans JScript est déjà une table de recherche."
Brian
Ok, donc vous dites d'ajouter une propriété à la référence d'élément et de la définir sur la référence d'objet datatable? Cela me semble également plus serré.
BobRodes