La fonction eval est un moyen puissant et facile de générer dynamiquement du code, alors quelles sont les mises en garde?
La fonction eval est un moyen puissant et facile de générer dynamiquement du code, alors quelles sont les mises en garde?
Réponses:
Une mauvaise utilisation d' eval ouvre votre code pour les attaques par injection
Le débogage peut être plus difficile (pas de numéro de ligne, etc.)
le code évalué s'exécute plus lentement (aucune possibilité de compiler / mettre en cache le code évalué)
Edit: Comme le souligne @Jeff Walden dans les commentaires, # 3 est moins vrai aujourd'hui qu'il ne l'était en 2008. Cependant, bien que la mise en cache des scripts compilés puisse se produire, cela ne sera limité qu'aux scripts répétés sans modification. Un scénario plus probable est que vous évaluez des scripts qui ont subi à chaque fois une légère modification et, en tant que tels, ne peuvent pas être mis en cache. Disons simplement que CERTAINS codes évalués s'exécutent plus lentement.
badHackerGuy'); doMaliciousThings();
et si vous prenez mon nom d'utilisateur, le concattez dans un script et l'évaluez dans les navigateurs d'autres personnes, je peux exécuter le javascript que je veux sur leurs machines (par exemple, les forcer à attribuer +1 à mes messages, publier leurs données sur mon serveur, etc.)
debugger;
déclaration à votre code source. Cela arrêtera l'exécution de votre programme sur cette ligne. Ensuite, vous pouvez ajouter des points d'arrêt de débogage comme s'il s'agissait simplement d'un autre fichier JS.
eval n'est pas toujours mauvais. Il y a des moments où c'est parfaitement approprié.
Cependant, eval est actuellement et historiquement massivement sur-utilisé par des gens qui ne savent pas ce qu'ils font. Cela inclut malheureusement des personnes écrivant des didacticiels JavaScript, et dans certains cas, cela peut en effet avoir des conséquences sur la sécurité - ou, plus souvent, de simples bugs. 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 probable que vous puissiez le faire de manière plus sûre, meilleure et plus propre.
Pour donner un exemple trop typique, pour définir la couleur d'un élément avec un identifiant stocké dans la variable 'potato':
eval('document.' + potato + '.style.color = "red"');
Si les auteurs du type de code ci-dessus avaient une idée des principes de base du fonctionnement des objets JavaScript, ils se seraient rendu compte que les crochets peuvent être utilisés à la place des noms de points littéraux, évitant ainsi la nécessité d'eval:
document[potato].style.color = 'red';
... qui est beaucoup plus facile à lire et moins potentiellement bogué.
(Mais alors, quelqu'un qui / vraiment / savait ce qu'il faisait disait:
document.getElementById(potato).style.color = 'red';
qui est plus fiable que la vieille astuce douteuse d'accéder aux éléments DOM directement à partir de l'objet document.)
JSON.parse()
place de eval()
JSON?
Je pense que c'est parce qu'il peut exécuter n'importe quelle fonction JavaScript à partir d'une chaîne. Son utilisation permet aux utilisateurs d'injecter plus facilement du code non autorisé dans l'application.
Deux points me viennent à l'esprit:
Sécurité (mais tant que vous générez la chaîne à évaluer vous-même, cela peut ne pas être un problème)
Performances: tant que le code à exécuter n'est pas connu, il ne peut pas être optimisé. (sur javascript et les performances, certainement la présentation de Steve Yegge )
eval
, puis ce petit morceau de code s'exécute sur le navigateur de l'utilisateur B
Passer l'entrée utilisateur à eval () est un risque pour la sécurité, mais chaque appel de eval () crée également une nouvelle instance de l'interpréteur JavaScript. Cela peut être un porc de ressources.
Surtout, c'est beaucoup plus difficile à maintenir et à déboguer. C'est comme un goto
. Vous pouvez l'utiliser, mais cela rend plus difficile de trouver des problèmes et plus difficile pour les personnes qui peuvent avoir besoin 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 autrement restreint - les sites de réseaux sociaux qui bloquent des fonctions JavaScript spécifiques peuvent parfois être dupés en les décomposant en un bloc eval -
eval('al' + 'er' + 't(\'' + 'hi there!' + '\')');
Donc, si vous cherchez à exécuter du code JavaScript 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 mieux loti relégué sur le plateau des `` hacks JavaScript délicats ''.
[]["con"+"struc"+"tor"]["con"+"struc"+"tor"]('al' + 'er' + 't(\'' + 'hi there!' + '\')')()
À moins que vous ne laissiez eval () un contenu dynamique (via cgi ou entrée), il est aussi sûr et solide que tous les autres JavaScript de votre page.
Avec le reste des réponses, je ne pense pas que les instructions eval puissent avoir une minimisation avancée.
C'est un risque de sécurité possible, il a une portée d'exécution différente et 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 .
Cependant, il est très utile et utilisé avec modération peut ajouter beaucoup de bonnes fonctionnalités.
À moins que vous ne soyez sûr à 100% que le code évalué provient d'une source fiable (généralement votre propre application), c'est un moyen infaillible d'exposer votre système à une attaque de script intersite.
Je sais que cette discussion est ancienne, mais j'aime vraiment cette approche de Google et je voulais partager ce sentiment avec les autres;)
L'autre chose est que plus vous obtenez mieux, plus vous essayez de comprendre et finalement vous ne croyez pas que quelque chose est bon ou mauvais juste parce que quelqu'un l'a dit :) C'est une vidéo très inspirante qui m'a aidé à réfléchir davantage :) LES BONNES PRATIQUES sont bonnes, mais ne les utilisez pas inconsidérément :)
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 JSON qui est revenu d'une requête XMLHttpRequest sur votre propre site, créé par votre code côté serveur de confiance, ce n'est probablement pas un problème.
Le code JavaScript côté client non fiable ne peut pas faire grand-chose de toute façon. À condition que la chose sur laquelle vous exécutez eval()
provienne d'une source raisonnable, tout va bien.
Cela réduit considérablement votre niveau de confiance en matière de sécurité.
Si vous voulez que l'utilisateur entre des fonctions logiques et évalue ET ET OU alors la fonction d'évaluation JavaScript est parfaite. Je peux accepter deux chaînes et eval(uate) string1 === string2
, etc.
Si vous repérez l'utilisation de eval () dans votre code, souvenez-vous du mantra «eval () est mauvais».
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é lors de l'exécution), il n'y a aucune raison d'utiliser eval (). Si le code est généré dynamiquement lors de l'exécution, il existe souvent un meilleur moyen d'atteindre l'objectif sans eval (). Par exemple, l'utilisation de la notation entre crochets pour accéder aux propriétés dynamiques est meilleure et plus simple:
// antipattern
var property = "name";
alert(eval("obj." + property));
// preferred
var property = "name";
alert(obj[property]);
En utilisant eval()
également des implications sur la sécurité, car vous pouvez exécuter du code (par exemple provenant du réseau) qui a été falsifié. Il s'agit d'un antipattern commun lorsqu'il s'agit d'une réponse JSON à partir d'une demande 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 pour vous assurer qu'elle est sûre et valide. Pour les navigateurs qui ne prennent pas en charge JSON.parse()
nativement, 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 le Function()
constructeur est, pour la plupart, similaire à l'utilisationeval()
et doit donc ê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 précaution. Ce pourrait être une construction puissante mais est souvent mal utilisée. Si vous devez absolument utilisereval()
, vous pouvez plutôt utiliser new Function ().
Il y a un petit avantage potentiel car le code évalué dans new Function () s'exécutera dans une étendue de fonction locale, donc toutes les variables définies avec var dans le code évalué ne deviendront pas globalement automatiquement.
Une autre façon d'empêcher les globaux automatiques consiste à encapsuler l'
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, la plupart du temps, il existe un meilleur moyen qui n'implique pas une nouvelle analyse du code à chaque exécution. Les fonctions anonymes ou les propriétés d'objet peuvent remplacer la plupart des utilisations d'eval et sont beaucoup plus sûres et plus rapides.
Cela peut devenir plus problématique à mesure que la prochaine génération de navigateurs sortira avec une certaine saveur d'un compilateur JavaScript. Le code exécuté via Eval peut ne pas fonctionner aussi bien que le reste de votre JavaScript contre ces nouveaux navigateurs. Quelqu'un devrait faire du profilage.
C'est l'un des bons articles qui parlent d'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 (). Il y a certainement des problèmes de clarté de code, de débogage et certainement de performances qui ne doivent pas être négligés. Mais vous ne devriez pas avoir peur de l'utiliser lorsque vous avez un cas où eval () a du sens. Essayez de ne pas l'utiliser d'abord, mais ne laissez personne vous faire peur en pensant 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 permet simplement de dire comment la chaîne que vous exécutez avec eval () est affectée par une partie malveillante. À la fin, vous exécuterez du code malveillant. Le pouvoir s'accompagne d'une grande responsabilité. Donc, utilisez-le judicieusement si vous l'utilisez. Ce n'est pas beaucoup lié à la fonction eval () mais cet article contient de très bonnes informations: http://blogs.popart.com/2009/07/javascript-injection-attacks/ Si vous recherchez les bases de eval () regardez ici: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval
Le moteur JavaScript dispose d'un certain nombre d'optimisations de performances qu'il effectue pendant la phase de compilation. Certains d'entre eux se résument à être en mesure d'analyser essentiellement statiquement le code pendant qu'il lexe, et de prédéterminer où se trouvent toutes les déclarations de variables et de fonctions, de sorte qu'il faut moins d'efforts pour résoudre les identificateurs pendant l'exécution.
Mais si le moteur trouve un eval (..) dans le code, il doit essentiellement supposer que toute sa connaissance de l'emplacement de l'identifiant peut être invalide, car il ne peut pas savoir au moment de lexing exactement quel code vous pouvez passer à eval (..) pour modifier la portée lexicale ou le contenu de l'objet vers lequel vous pouvez passer pour créer une nouvelle portée lexicale à consulter.
En d'autres termes, au sens pessimiste, la plupart de ces optimisations qu'il ferait sont inutiles si eval (..) est présent, il n'effectue donc 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
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()
il le transforme en code exécutable. j'ai trouvéeval()
dans cette situation particulière l'opposé exact du mal.
Fondamentalement 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({}))
La performance de eval()
ne sont pas un problème dans une situation comme celle-ci, car vous n'avez besoin d'interpréter la chaîne générée qu'une seule fois, puis de réutiliser la sortie exécutable plusieurs fois.
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()
en javascript qui est exécuté dans les navigateurs. * (Mise en garde)
Tous les navigateurs modernes ont une console de développeur où vous pouvez de toute façon exécuter du javascript arbitraire et tout développeur semi-intelligent peut regarder votre source JS et mettre tout ce dont il a besoin dans la console de développement pour faire ce qu'il souhaite.
* Tant que vos points de terminaison de serveur ont la validation et la désinfection correctes des valeurs fournies par l'utilisateur, peu importe ce qui est analysé et évalué dans votre javascript côté client.
eval()
Cependant, si vous demandez s'il est approprié d'utiliser en PHP, la réponse est NON , à moins que vous ne mettiez en liste blanche les valeurs qui peuvent être transmises à votre instruction eval.
Je n'essaierai pas de réfuter ce qui a été dit jusqu'ici, mais je proposerai cette utilisation d'eval () qui (pour autant que je sache) ne peut être faite autrement. Il y a probablement d'autres façons de coder cela, et probablement des façons de l'optimiser, mais cela se fait à la main et sans cloches et sifflets pour plus de clarté afin d'illustrer une utilisation d'eval qui n'a vraiment pas d'autre alternative. C'est-à-dire: des noms d'objets créés par programmation dynamiques (ou plus précisément) (par opposition aux valeurs).
//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: au fait, je ne suggérerais pas (pour toutes les raisons de sécurité mentionnées ci-dessus) que vous basiez vos noms d'objet sur les entrées utilisateur. Je ne peux cependant imaginer aucune bonne raison pour laquelle vous voudriez faire cela. Pourtant, j'ai pensé que je ferais remarquer que ce ne serait pas une bonne idée :)
namespaceToTest[namespaceParts[i]]
, pas besoin d'évaluation ici, donc if(typeof namespaceToTest[namespaceParts[i]] === 'undefined') { namespaceToTest[namespaceParts[i]] = {};
la seule différence pour leelse namespaceToTest = namespaceToTest[namespaceParts[i]];
Collecte des ordures
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, il le conserve donc jusqu'à ce que la page soit rechargée. Pas trop mal si vos utilisateurs ne sont que brièvement sur votre page, mais cela peut être un problème pour les webapps.
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 entraîne le stockage d'une petite quantité de mémoire jusqu'à la fin de l'application. C'est pire quand le script évalué est une fonction géante, et appelé intervalle.