Pourquoi l'utilisation de la fonction JavaScript eval est-elle une mauvaise idée?

StackOverflow https://stackoverflow.com/questions/86513

  •  01-07-2019
  •  | 
  •  

Question

La fonction eval est un moyen simple et puissant de générer du code de manière dynamique. Quelles sont donc les mises en garde?

Était-ce utile?

La solution

  1. L'utilisation inappropriée de eval ouvre votre code pour les attaques par injection

  2. Le débogage peut être plus difficile (pas de numéro de ligne, etc.)

  3. le code évalué s'exécute plus lentement (aucune possibilité de compiler / mettre en cache le code évalué)

Modifier: Comme le souligne @Jeff Walden dans les commentaires, le n ° 3 est moins vrai aujourd'hui qu'en 2008. Toutefois, même si une certaine mise en cache de scripts compilés peut survenir, elle ne sera limitée qu'aux scripts qui sont répétés avec aucune modification. Un scénario plus probable est que vous évaluez des scripts qui ont subi de légères modifications à chaque fois et qui, en tant que tels, ne peuvent pas être mis en cache. Disons simplement que CERTAINS codes évalués s'exécutent plus lentement.

Autres conseils

eval n'est pas toujours mauvais. Il y a des moments où cela convient parfaitement.

Cependant, eval est actuellement et historiquement très utilisé par des personnes qui ne savent pas ce qu’elles font. Cela inclut malheureusement les personnes qui écrivent des tutoriels sur JavaScript et dans certains cas, cela peut effectivement avoir des conséquences sur la sécurité - ou, le plus souvent, de simples bogues. Donc, plus nous pouvons faire pour jeter un point d'interrogation sur eval, mieux c'est. Chaque fois que vous utilisez eval, vous devez vérifier ce que vous faites, car il est fort probable que vous le fassiez de manière plus efficace, plus sûre et plus propre.

Pour donner un exemple trop typique, définir la couleur d'un élément avec un identifiant stocké dans la variable 'patate':

eval('document.' + potato + '.style.color = "red"');

Si les auteurs du type de code ci-dessus avaient une idée du fonctionnement de base des objets JavaScript, ils se seraient rendus compte que les crochets pouvaient être utilisés à la place des noms de points littéraux, éliminant ainsi le besoin d'utiliser eval:

document[potato].style.color = 'red';

... ce qui est beaucoup plus facile à lire ainsi que moins potentiellement bogué.

(Mais alors, quelqu'un qui / savait vraiment ce qu'il était en train de faire dirait:

document.getElementById(potato).style.color = 'red';

qui est plus fiable que l’ancienne astuce douteuse consistant à accéder aux éléments DOM directement à partir de l’objet document.)

Je pense que c'est parce qu'il peut exécuter n'importe quelle fonction JavaScript à partir d'une chaîne. Son utilisation facilite l’injection de code non autorisé dans l’application.

Deux points me viennent à l’esprit:

  1. Sécurité (mais tant que vous générez la chaîne à évaluer vous-même, cela pourrait ne pas être un problème)

  2. Performances: tant que le code à exécuter n'est pas inconnu, il ne peut pas être optimisé. (à propos de javascript et de performances, certainement la présentation de Steve Yegge )

Le fait de passer une entrée utilisateur à eval () pose un risque pour la sécurité, mais chaque appel de eval () crée une nouvelle instance de l'interpréteur JavaScript. Cela peut être une ressource de porc.

C’est généralement un problème uniquement si vous transmettez une entrée utilisateur eval.

Principalement, il est beaucoup plus difficile de maintenir et de déboguer. C'est comme un goto . Vous pouvez l’utiliser, mais il est plus difficile de trouver des problèmes et plus difficile pour les personnes susceptibles d’apporter des modifications plus tard.

Une chose à garder à l'esprit est que vous pouvez souvent utiliser eval () pour exécuter du code dans un environnement normalement restreint - les sites de réseaux sociaux bloquant des fonctions JavaScript spécifiques peuvent parfois être trompés en les divisant en un bloc eval -

eval('al' + 'er' + 't(\'' + 'hi there!' + '\')');

Donc, si vous souhaitez exécuter du code JavaScript là où il pourrait ne pas être autorisé ( Myspace , je te regarde ...) alors eval () peut être une astuce utile.

Cependant, pour toutes les raisons mentionnées ci-dessus, vous ne devriez pas l'utiliser pour votre propre code, où vous avez un contrôle total - ce n'est tout simplement pas nécessaire et qui est mieux relégué au rayon des "astuces javascript".

Sauf si vous laissez à eval () un contenu dynamique (via cgi ou une entrée), il est aussi sûr et solide que tout autre code JavaScript de votre page.

Avec le reste des réponses, je ne pense pas que les déclarations eval puissent avoir une minimisation avancée.

C’est un risque possible pour la sécurité, son champ d’exécution est différent et il est assez inefficace, car il crée un environnement de script entièrement nouveau pour l’exécution du code. Cliquez ici pour plus d’informations: eval .

Il est très utile, cependant, et utilisé avec modération peut ajouter beaucoup de bonnes fonctionnalités.

Si vous n'êtes pas sûr à 100% que le code en cours d'évaluation provient d'une source fiable (généralement votre propre application), il s'agit d'un moyen sûr d'exposer votre système à une attaque de script intersite.

Je sais que cette discussion est ancienne, mais j'aime beaucoup ceci par Google et souhaitait partager ce sentiment avec d'autres;)

L’autre chose, c’est que mieux vous gagnez, plus vous essayez de comprendre et enfin, vous ne croyez pas que quelque chose est bon ou mauvais simplement parce que quelqu'un l’a dit :) Il s'agit d'une vidéo très inspirante qui m'a permis de penser plus seul :) BONNES PRATIQUES: bien, mais ne les utilisez pas sans réfléchir:)

Ce n'est pas nécessairement si mauvais à condition de savoir dans quel contexte vous l'utilisez.

Si votre application utilise eval () pour créer un objet à partir d'un fichier JSON renvoyé d'un XMLHttpRequest sur votre propre site, créé par votre code côté serveur sécurisé, ce n'est probablement pas un problème.

De toute façon, un code JavaScript non approuvé côté client ne peut pas faire grand chose. Si vous exécutez eval () sur une source raisonnable, tout va bien.

Cela réduit considérablement votre niveau de confiance en la sécurité.

Si vous souhaitez que l'utilisateur entre certaines fonctions logiques et évalue AND, la fonction JavaScript eval est parfaite. Je peux accepter deux chaînes et eval (uate) string1 === chaine2 , etc.

Si vous remarquez l'utilisation de eval () dans votre code, rappelez-vous le mantra «eval () est diabolique».

Ceci function prend une chaîne arbitraire et l'exécute en tant que code JavaScript. Quand le code est entré Si la question est connue à l’avance (non déterminée au moment de l’exécution), il n’ya aucune raison eval (). Si le code est généré dynamiquement au moment de l’exécution, il existe souvent un meilleur moyen de atteindre l'objectif sans eval (). Par exemple, en utilisant simplement la notation entre crochets pour accéder aux propriétés dynamiques est meilleur et plus simple:

// antipattern
var property = "name";
alert(eval("obj." + property));

// preferred
var property = "name";
alert(obj[property]);

L'utilisation de eval () a également des conséquences en termes de sécurité, car vous exécutez peut-être du code (par exemple, exemple provenant du réseau) qui a été falsifié. C'est un anti-modèle commun lorsqu'il s'agit d'une réponse JSON à partir d'une requête Ajax. Dans ces cas il est préférable d’utiliser les méthodes intégrées des navigateurs pour analyser la réponse JSON afin de que c’est sûr et valide. Pour les navigateurs qui ne prennent pas en charge JSON.parse () de manière native, vous pouvez utiliser une bibliothèque de JSON.org.

Il est également important de noter que le passage de chaînes à setInterval () , setTimeout () , et le constructeur Function () est généralement similaire à l'utilisation de eval () et par conséquent devrait être évité.

Dans les coulisses, JavaScript doit encore évaluer et exécuter la chaîne que vous passez en tant que code de programmation:

// antipatterns
setTimeout("myFunc()", 1000);
setTimeout("myFunc(1, 2, 3)", 1000);

// preferred
setTimeout(myFunc, 1000);
setTimeout(function () {
myFunc(1, 2, 3);
}, 1000);

L'utilisation du nouveau constructeur Function () est similaire à eval () et doit être abordée. avec soin. Ce pourrait être une construction puissante mais est souvent mal utilisée. Si vous devez absolument utilisez eval () , vous pouvez envisager d'utiliser new Function () à la place.

Il y a un petit potentiel avantage parce que le code évalué dans new Function () fonctionnera dans une fonction locale portée, donc toutes les variables définies avec var dans le code en cours d’évaluation ne deviendront pas globals automatiquement.

Un autre moyen d'éviter les globales automatiques consiste à envelopper le eval () appel dans une fonction immédiate.

Outre les problèmes de sécurité possibles si vous exécutez du code soumis par l'utilisateur, il existe généralement un meilleur moyen de ne pas ré-analyser le code à chaque exécution. Les fonctions anonymes ou les propriétés d'objet peuvent remplacer la plupart des utilisations de eval et sont beaucoup plus sûres et plus rapides.

Cela pourrait devenir plus problématique si la prochaine génération de navigateurs propose une sorte de compilateur JavaScript. Le code exécuté via Eval risque de ne pas fonctionner aussi bien que le reste de votre JavaScript par rapport à ces nouveaux navigateurs. Quelqu'un devrait faire du profilage.

C’est l’un des bons articles sur eval et sur le fait que ce n’est pas un mal: http://www.nczonline.net/ blog / 2013/06/25 / eval-isnt-evil-just-incompris /

  

Je ne dis pas que vous devriez aller en manquer et commencer à utiliser eval ()   partout. En fait, il y a très peu de bons cas d'utilisation pour exécuter   eval () du tout. La clarté du code pose assurément des problèmes,   débogabilité, et certainement des performances à ne pas négliger.   Mais vous ne devriez pas avoir peur de l'utiliser quand vous avez un cas où   eval () a du sens. Essayez de ne pas l'utiliser en premier, mais ne laissez personne effrayer   vous pensez que votre code est plus fragile ou moins sécurisé lorsque eval ()   est utilisé de manière appropriée.

eval () est très puissant et peut être utilisé pour exécuter une instruction JS ou évaluer une expression. Mais la question ne concerne pas les utilisations de eval (), mais simplement en quoi la chaîne que vous exécutez avec eval () est affectée par une partie malveillante. À la fin, vous exécuterez du code malveillant. Avec le pouvoir vient une grande responsabilité. Donc, utilisez-le judicieusement si vous l'utilisez.  Ce n'est pas lié beaucoup à la fonction eval () mais cet article a de très bonnes informations:   http://blogs.popart.com/2009/07/javascript-injection- attaques / Si vous recherchez les bases de eval (), regardez ici: https://developer.mozilla.org/en-US / docs / Web / JavaScript / Référence / Global_Objects / eval

Je ne tenterai pas de réfuter quoi que ce soit qui a été dit auparavant, mais je proposerai cette utilisation de eval () qui (pour autant que je sache) ne peut pas être fait autrement. Il y a probablement d'autres moyens de coder cela, et probablement de l'optimiser, mais cela se fait dans la longueur, sans clochettes, pour plus de clarté, pour illustrer une utilisation de eval qui n'a pas d'autre alternative. C’est-à-dire: des noms d’objets créés par programmation (ou plus précisément, de manière dynamique (ou plus précisément).

//Place this in a common/global JS lib:
var NS = function(namespace){
    var namespaceParts = String(namespace).split(".");
    var namespaceToTest = "";
    for(var i = 0; i < namespaceParts.length; i++){
        if(i === 0){
            namespaceToTest = namespaceParts[i];
        }
        else{
            namespaceToTest = namespaceToTest + "." + namespaceParts[i];
        }

        if(eval('typeof ' + namespaceToTest) === "undefined"){
            eval(namespaceToTest + ' = {}');
        }
    }
    return eval(namespace);
}


//Then, use this in your class definition libs:
NS('Root.Namespace').Class = function(settings){
  //Class constructor code here
}
//some generic method:
Root.Namespace.Class.prototype.Method = function(args){
    //Code goes here
    //this.MyOtherMethod("foo"));  // => "foo"
    return true;
}


//Then, in your applications, use this to instantiate an instance of your class:
var anInstanceOfClass = new Root.Namespace.Class(settings);

EDIT: en passant, je ne suggérerais pas (pour toutes les raisons de sécurité soulignées jusqu'à présent) que vous fondiez les noms d'objet sur la saisie de l'utilisateur. Je ne peux cependant pas imaginer de bonne raison de vouloir faire cela. Pourtant, je pensais le préciser, ce ne serait pas une bonne idée:)

Le moteur JavaScript dispose d'un certain nombre d'optimisations de performances qu'il exécute pendant la phase de compilation. Certaines d’entre elles se résument à la possibilité d’analyser statiquement le code tel qu’il est lexical et de déterminer à l’avance où se trouvent toutes les déclarations de variables et de fonctions, de sorte que la résolution des identificateurs au cours de l’exécution nécessite moins d’effort.

Mais si le moteur trouve un eval (..) dans le code, il doit en principe supposer que toutes ses informations sur l'emplacement de l'identificateur peuvent être invalides, car il ne peut pas savoir exactement quel code vous allez transmettre à eval ( ..) pour modifier la portée lexicale ou le contenu de l'objet que vous pouvez passer à avec pour créer une nouvelle portée lexicale à consulter.

En d’autres termes, au sens pessimiste du terme, la plupart de ces optimisations seraient inutiles si eval (..) est présent, il n’exécute donc pas du tout les optimisations.

Cela explique tout.

Référence:

https: //github.com/getify/You-Dont-Know-JS/blob/master/scope%20&%20closures/ch2.md#eval

https: //github.com/getify/You-Dont-Know-JS/blob/master/scope%20&%20closures/ch2.md#performance

Ce n’est pas toujours une mauvaise idée. Prenons par exemple la génération de code. J'ai récemment écrit une bibliothèque appelée Hyperbars qui jette un pont entre virtual-dom et guidons . Pour ce faire, il analyse un modèle de guidon et le convertit en hyperscript , qui est ensuite utilisé par virtual-dom. L’hyperscript est d’abord généré sous forme de chaîne, puis, avant de le renvoyer, eval () pour le transformer en code exécutable. J'ai trouvé eval () dans cette situation particulière, l'exact opposé du mal.

Fondamentalement de

<div>
    {{#each names}}
        <span>{{this}}</span>
    {{/each}}
</div>

Pour cela

(function (state) {
    var Runtime = Hyperbars.Runtime;
    var context = state;
    return h('div', {}, [Runtime.each(context['names'], context, function (context, parent, options) {
        return [h('span', {}, [options['@index'], context])]
    })])
}.bind({}))

Les performances de eval () ne posent pas de problème dans une situation comme celle-ci, car il vous suffit d'interpréter la chaîne générée une seule fois, puis de réutiliser plusieurs fois le résultat de l'exécutable.

Vous pouvez voir comment la génération de code a été réalisée si vous êtes curieux ici .

J'irais jusqu'à dire que cela n'a pas vraiment d'importance si vous utilisez eval () dans javascript, qui est exécuté dans les navigateurs. * (mise en garde)

Tous les navigateurs modernes ont une console de développeur sur laquelle vous pouvez exécuter du code javascript de toute façon et tout développeur semi-intelligent peut consulter votre source JS et en placer les éléments dont il a besoin dans la console de développement pour faire ce qu'il souhaite.

* Tant que les noeuds finaux de votre serveur disposent de la validation correcte & amp; Pour la désinfection des valeurs fournies par l’utilisateur, peu importe ce qui est analysé et évalué dans votre javascript côté client.

Si vous deviez demander s'il est approprié d'utiliser eval () en PHP, la réponse est NON , sauf si vous liste blanche valeurs pouvant être passées à votre déclaration eval.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top