web-dev-qa-db-fra.com

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

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?

512
Brian Singh
  1. Une mauvaise utilisation de eval ouvre votre code pour les attaques par injection

  2. Débogage peut être plus difficile (pas de numéros de ligne, etc.)

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

Edit: Comme @Jeff Walden le souligne dans les commentaires, le n ° 3 est moins vrai aujourd'hui qu'en 2008. Cependant, même si une mise en cache de scripts compilés peut se produire, cela ne se limitera qu'aux scripts répétés sans modification. Un scénario plus probable est que vous évaluez des scripts qui ont subi de légères modifications à chaque fois et ne peuvent donc pas être mis en cache. Disons simplement que CERTAINS codes évalués s'exécutent plus lentement.

374
Prestaul

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 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 bien trop typique, définir la couleur d'un élément avec un identifiant stocké dans la variable 'pomme de terre':

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, évitant ainsi la nécessité d'utiliser eval:

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

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

(Mais alors, quelqu'un qui savait vraiment ce qu'il faisait 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.)

342
bobince

Je crois 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.

37
kemiller2002

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. Performance: tant que le code à exécuter n'est pas inconnu, il ne peut pas être optimisé. (à propos de javascript et de performance, certainement présentation de Steve Yegge )

26
xtofl

Le fait de passer une entrée utilisateur à eval () constitue 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 porcine.

20
Andrew Hedges

Ce n'est généralement un problème que si vous transmettez une entrée d'utilisateur eval.

18
Mark Biek

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 de devoir apporter des modifications ultérieurement.

15
Brian

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

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

Donc, si vous cherchez à exécuter du code JavaScript là où il ne serait pas autorisé autrement ( Myspace , je vous regarde ...), 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".

13
matt lohkamp

À moins de laisser à eval () un contenu dynamique (via cgi ou entrée), il est aussi sûr et solide que tout autre JavaScript de votre page.

11
Thevs

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

7
Paul Mendoza

C'est un risque de sécurité possible, 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. Voir ici pour plus d’informations: eval .

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

6
Tom

Je sais que cette discussion est ancienne, mais j'aime beaucoup cette approche de Google et je voulais partager ce sentiment avec d'autres;)

L'autre chose, c'est que mieux on gagne, plus on essaie de comprendre et finalement, on ne croit tout simplement pas que quelque chose est bon ou mauvais simplement parce que quelqu'un l'a dit :) C'est très inspirant vidéo ça m'a aidé à penser plus par moi-même :) BONNES PRATIQUES c'est bien, mais ne les utilisez pas inconsciemment :)

5
op1ekun

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

5
John Topley

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éé à l'aide du code côté serveur de confiance, ce n'est probablement pas un problème. .

Un code JavaScript non approuvé côté client ne peut pas faire grand chose de toute façon. Pourvu que la chose que vous exécutez eval() vienne d'une source raisonnable, tout va bien.

5
MarkR

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

4
David Plumpton

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

4
Ian

Si vous remarquez l'utilisation de eval () dans votre code, souvenez-vous du mantra "eval () est diabolique".

Cette fonction prend une chaîne arbitraire et l'exécute en tant que code JavaScript. Lorsque le code en question est connu à l’avance (non déterminé au moment de l’exécution), il n’ya aucune raison d’utiliser eval (). Si le code est généré dynamiquement au moment de l’exécution, il existe souvent un meilleur moyen d’atteindre l’objectif sans eval (). Par exemple, utiliser 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 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 s’assurer qu’elle est sûre et valide. Pour les navigateurs qui ne supportent pas JSON.parse() de manière native, vous pouvez utiliser une bibliothèque de JSON.org.

Il est également important de se rappeler que le passage de chaînes à setInterval(), setTimeout() et au constructeur Function() est généralement similaire à l'utilisation de eval() et doit donc être évité.

En coulisse, JavaScript doit encore évaluer et exécuter la chaîne que vous transmettez 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 précaution. Ce pourrait être une construction puissante mais est souvent mal utilisée. Si vous devez absolument utiliser eval(), vous pouvez envisager d'utiliser new Function () à la place.

Il y a un petit avantage potentiel, car le code évalué dans new Function () fonctionnera dans une portée de fonction locale. Par conséquent, les variables définies avec var dans le code en cours d'évaluation ne deviendront pas automatiquement des globales.

Une autre façon d'empêcher les globales automatiques consiste à envelopper l'appel eval() dans une fonction immédiate.

3
12345678

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.

2
Matthew Crumley

Cela pourrait devenir un problème d'autant plus grand que la prochaine génération de navigateurs propose un compilateur JavaScript. Le code exécuté via Eval risque de ne pas fonctionner aussi bien que le reste de votre JavaScript par rapport à ces navigateurs plus récents. Quelqu'un devrait faire du profilage.

2
Brian

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. Cela n’a pas grand-chose à voir avec la fonction eval (), mais cet article contient de très bonnes informations: http://blogs.popart.com/2009/07/javascript-injection-attacks/ Si vous êtes à la recherche Pour connaître les bases de eval (), regardez ici: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval

2
Phi

C'est l'un des bons articles sur eval et comment ce n'est pas un mal: http://www.nczonline.net/blog/2013/06/25/eval-isnt-evil-just-misunderstood/

Je ne dis pas que vous devriez vous épuiser et commencer à utiliser eval () partout. En fait, il existe très peu de bons cas d'utilisation pour exécuter eval (). La clarté du code, la capacité de débogage et les performances ne doivent pas être négligées. Mais vous ne devriez pas avoir peur de l’utiliser lorsque vous avez un cas où eval () a un sens. Essayez de ne pas l’utiliser d’abord, mais ne laissez personne vous effrayer en pensant que votre code est plus fragile ou moins sûr lorsque eval () est utilisé correctement.

2
Amr Elgarhy

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 comble le fossé entre virtual-dom et guidon . 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 et, 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.

Essentiellement de

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

Pour ça

(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 sont pas un problème dans une situation comme celle-ci, car il vous suffit d'interpréter la chaîne générée une fois, puis de réutiliser plusieurs fois la sortie de l'exécutable.

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

1
Wikened

Je ne tenterai pas de réfuter tout ce 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: 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é indiquées précédemment) de baser 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 que ça ne serait pas une bonne idée :)

1
Carnix

J'irais même jusqu'à dire que le fait d'utiliser eval() en javascript, qui est exécuté dans les navigateurs, n'a pas vraiment d'importance. * (Mise en garde)

Tous les navigateurs modernes ont une console de développeur sur laquelle vous pouvez exécuter du javascript arbitraire de toute façon. 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 points finaux de votre serveur ont la validation et la purification correctes 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() dans PHP, cependant, la réponse est NO, sauf si vous liste blanche tout les valeurs qui peuvent être passées à votre déclaration eval.

1
Adam Copley

Le moteur JavaScript dispose d'un certain nombre d'optimisations de performances qu'il effectue pendant la phase de compilation. Certaines d'entre elles se résument à la capacité d'analyser de manière statique le code tel qu'il se présente, et de déterminer à l'avance l'emplacement de toutes les déclarations de variable et de fonction, 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 pouvez transmettre à eval (..). modifier la portée lexicale ou le contenu de l'objet que vous pouvez utiliser pour créer une nouvelle portée lexicale à consulter.

En d’autres termes, au sens pessimiste, la plupart de ces optimisations seraient inutiles si eval (..) est présent, de sorte qu’il n’effectue tout simplement pas 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

1
hkasera

Garbage collection

La récupération de place des navigateurs n'a aucune idée si le code qui est évalué peut être supprimé de la mémoire afin qu'il soit simplement stocké jusqu'à ce que la page soit rechargée. Pas si mal si vos utilisateurs ne sont que sur votre page sous peu, mais cela peut être un problème pour webapp.

Voici un script pour démontrer le problème

https://jsfiddle.net/CynderRnAsh/qux1osnw/

document.getElementById("evalLeak").onclick = (e) => {
  for(let x = 0; x < 100; x++) {
    eval(x.toString());
  }
};

Quelque chose d'aussi simple que le code ci-dessus provoque le stockage d'une petite quantité de mémoire jusqu'à la mort de l'application. Ceci est pire lorsque le script évalué est une fonction géante et appelée intervalle.

0
J D