Quand eval () de JavaScript n'est-il pas mal?


264

J'écris du code JavaScript pour analyser les fonctions entrées par l'utilisateur (pour les fonctionnalités de type tableur). Après avoir analysé la formule, je pouvais le convertir en JavaScript et l'exécuter eval()pour donner le résultat.

Cependant, j'ai toujours hésité à l'utiliser eval()si je peux l'éviter parce que c'est mal (et, à tort ou à raison, j'ai toujours pensé que c'était encore plus mal en JavaScript, car le code à évaluer pourrait être modifié par l'utilisateur ).

Alors, quand c'est OK de l'utiliser?


5
La plupart des bibliothèques JSON n'utilisent pas eval sous le capot, exactement pour se protéger contre les risques de sécurité.
Sean McMillan

11
@Sean - JQuery et Prototype utilisent eval (JQuery l'utilise via une nouvelle fonction)
plodder

5
@plodder - Où obtenez-vous vos informations? jQuery utilise le JSON.parse () natif depuis 1.4 (retour en 1/2010)! Voyez par vous-même: code.jquery.com/jquery-1.4.js
ken

3
"Evidemment, il faut utiliser eval () pour analyser JSON" - ce n'est pas vrai, au contraire - il ne faut pas utiliser eval pour analyser JSON! Utilisez le script json2.js de Douglas Crockfords (créateur de JSON) sur json.org !
TMS

11
@Tomas l'ironie étant que json2.js utilise eval pour analyser JSON
tobyodavies

Réponses:


262

Je voudrais prendre un moment pour répondre à la prémisse de votre question - que eval () est " diabolique ". Le mot " evil ", tel qu'il est utilisé par les gens du langage de programmation, signifie généralement "dangereux", ou plus précisément "capable de causer beaucoup de mal avec une commande simple". Alors, quand est-ce OK d'utiliser quelque chose de dangereux? Lorsque vous savez quel est le danger et lorsque vous prenez les précautions appropriées.

Au point, regardons les dangers de l'utilisation de eval (). Il y a probablement beaucoup de petits dangers cachés comme tout le reste, mais les deux gros risques - la raison pour laquelle eval () est considéré comme mauvais - sont les performances et l'injection de code.

  • Performance - eval () exécute l'interpréteur / compilateur. Si votre code est compilé, c'est un gros succès, car vous devez appeler un compilateur éventuellement lourd au milieu de l'exécution. Cependant, JavaScript est encore principalement un langage interprété, ce qui signifie que l'appel à eval () n'est pas un gros problème de performances dans le cas général (mais voir mes remarques spécifiques ci-dessous).
  • Injection de code - eval () exécute potentiellement une chaîne de code avec des privilèges élevés. Par exemple, un programme s'exécutant en tant qu'administrateur / root ne voudra jamais de l'entrée utilisateur eval (), car cette entrée peut potentiellement être «rm -rf / etc / important-file» ou pire. Encore une fois, JavaScript dans un navigateur n'a pas ce problème, car le programme s'exécute de toute façon dans le propre compte de l'utilisateur. Le JavaScript côté serveur pourrait avoir ce problème.

Passons à votre cas spécifique. D'après ce que je comprends, vous générez les chaînes vous-même, donc en supposant que vous faites attention à ne pas générer une chaîne comme "rm -rf quelque chose d'important", il n'y a pas de risque d'injection de code (mais n'oubliez pas, c'est très très difficile à garantir dans le cas général). De plus, si vous utilisez le navigateur, l'injection de code est un risque assez mineur, je crois.

En ce qui concerne les performances, vous devrez les comparer à la facilité de codage. À mon avis, si vous analysez la formule, vous pourriez aussi bien calculer le résultat pendant l'analyse plutôt que d'exécuter un autre analyseur (celui à l'intérieur de eval ()). Mais il peut être plus facile de coder en utilisant eval (), et les performances seront probablement imperceptibles. Il semble que eval () dans ce cas n'est pas plus mauvais que n'importe quelle autre fonction qui pourrait éventuellement vous faire gagner du temps.


78
Vous n'abordez pas le problème du code qui utilise eval étant difficile à déboguer
bobobobo

48
L'injection de code est un problème très grave pour javascript si vous êtes préoccupé par les données de votre utilisateur. Le code injecté s'exécutera (dans le navigateur) comme s'il provenait de votre site, le laissant faire n'importe quelle sorte de shenanigan que l'utilisateur pourrait faire manuellement. Si vous autorisez un code (tiers) à entrer sur votre page, il peut commander des choses au nom de votre client, ou changer son gravatar, ou tout ce qu'il pourrait faire via votre site. Soyez très prudent. Laisser les pirates posséder vos clients est tout aussi mauvais que les laisser posséder votre serveur.
Sean McMillan

71
Si les données proviennent de votre serveur et c'est quelque chose que vous, le développeur a généré, il n'y a aucun mal à utiliser eval (). Le vrai mal, c'est de croire tout ce que vous lisez. Vous voyez beaucoup de gens dire que eval () est mauvais et ils ne savent pas pourquoi, sauf qu'ils l'ont lu quelque part.
Vince Panuccio

42
@Sean McMillan: Je veux vous croire, mais si quelqu'un va intercepter et changer le javascript à eval()partir de votre serveur, il pourrait aussi simplement changer la source de la page en premier lieu, et également prendre le contrôle des informations de l'utilisateur. . . Je ne vois pas la différence.
Walt W

20
Re "Injection de code - ... Encore une fois, JavaScript dans un navigateur n'a pas ce problème," & "De plus, si vous utilisez le navigateur, l'injection de code est un risque assez mineur, je crois." Suggérez-vous que l'injection de code dans le navigateur n'est pas un problème? XSS figure dans le top 3 des vulns de la liste des 10 meilleurs OWASP depuis plusieurs années.
Mike Samuel

72

eval()n'est pas mal. Ou, si c'est le cas, c'est mauvais de la même manière que la réflexion, les E / S de fichier / réseau, le filetage et IPC sont "mauvais" dans d'autres langues.

Si, pour votre objectif , eval()est plus rapide que l'interprétation manuelle, ou rend votre code plus simple, ou plus clair ... alors vous devriez l'utiliser. Si ni l'un ni l'autre, vous ne devriez pas. Aussi simple que cela.


5
Un de ces objectifs pourrait être de générer du code optimisé qui serait soit trop long soit trop répétitif pour être écrit à la main. Le genre de choses qui, dans LISP, nécessiteraient une macro.
wberry

5
Il s'agit d'un conseil si général qu'il pourrait s'appliquer à n'importe quel bloc de code existant. Cela n'ajoute vraiment rien à cette question; en particulier, cela n'aide personne à venir ici pour déterminer si son utilisation particulière est problématique ou non.
jpmc26

2
Plus rapide, plus simple, plus clair ... Cette réponse ne couvre pas assez bien les implications pour la sécurité.
Ruud Helderman

55

Lorsque vous faites confiance à la source.

Dans le cas de JSON, il est plus ou moins difficile de falsifier la source, car il provient d'un serveur Web que vous contrôlez. Tant que le JSON lui-même ne contient aucune donnée téléchargée par un utilisateur, il n'y a pas d'inconvénient majeur à utiliser eval.

Dans tous les autres cas, je ferais beaucoup d'efforts pour m'assurer que les données fournies par l'utilisateur sont conformes à mes règles avant de les envoyer à eval ().


13
Une chaîne json doit toujours être testée par rapport à la grammaire json avant de l'utiliser dans eval (). Ainsi, la chaîne json "{foo: alert ('XSS')}" ne passerait pas car "alert ('XSS')" n'est pas une valeur appropriée.
Gumbo

3
Eh bien, utilisez HTTPS, alors. OTOH: man-in-the-middle n'est pas le scénario d'attaque typique pour l'application web de variétés de jardins, contrairement à l'écriture de scripts intersites.
Tomalak

7
evaln'analysera pas non plus correctement toutes les chaînes JSON valides. Par exemple, JSON.parse(' "\u2028" ') === "\u2028"mais eval(' "\u2028" ')lève une exception car U + 2028 est une nouvelle ligne en JavaScript mais ce n'est pas une nouvelle ligne en ce qui concerne JSON.
Mike Samuel

1
@Justin - si le protocole est compromis, eh bien, en général, le chargement de la page initiale aurait été envoyé via ce même protocole, puis c'est un point discutable car le client est déjà aussi compromis que possible.
antinome

1
Magnifiquement dit @Tomalak, je l'ai mentionné dans ma réponse dès maintenant! Impressionnant!
NiCk Newman

25

Mettons-nous de vrais gens:

  1. Chaque navigateur majeur dispose désormais d'une console intégrée que votre pirate potentiel peut utiliser en abondance pour invoquer n'importe quelle fonction de n'importe quelle valeur - pourquoi s'embêter à utiliser une instruction eval - même s'il le pouvait?

  2. S'il faut 0,2 seconde pour compiler 2000 lignes de JavaScript, quelle est ma dégradation des performances si j'évalue quatre lignes de JSON?

Même l'explication de Crockford pour «eval is evil» est faible.

eval is Evil, La fonction eval est la fonctionnalité la plus mal utilisée de JavaScript. L'éviter

Comme Crockford lui-même pourrait le dire "Ce genre de déclaration a tendance à générer une névrose irrationnelle. Ne l'achetez pas."

Comprendre eval et savoir quand il pourrait être utile est beaucoup plus important. Par exemple, eval est un outil judicieux pour évaluer les réponses du serveur qui ont été générées par votre logiciel.

BTW: Prototype.js appelle eval directement cinq fois (y compris dans evalJSON () et evalResponse ()). jQuery l'utilise dans parseJSON (via le constructeur Function).


10
JQuery utilise la fonction JSON.parse intégrée du navigateur si elle est disponible (qui est beaucoup plus rapide et plus sûre), en utilisant eval uniquement comme mécanisme de secours. La déclaration "eval is evil" est une bonne ligne directrice.
jjmontes

30
Re "Chaque navigateur majeur a maintenant une console intégrée ...". L'injection de code est un problème lorsqu'un utilisateur peut entrer du code qui est ensuite exécuté dans le navigateur d'un autre utilisateur. Les consoles de navigateur ne permettent pas en elles-mêmes à un utilisateur d'exécuter du code dans le navigateur d'un autre utilisateur, elles ne sont donc pas pertinentes pour décider s'il vaut la peine de se protéger contre l'injection de code.
Mike Samuel

28
"Tous les principaux navigateurs ont maintenant une console intégrée ... pourquoi se donneraient-ils la peine d'utiliser une instruction eval?" - Vous êtes loin de la marque. Je vous suggère de modifier la réponse. La capacité d'un utilisateur à injecter du code pouvant s'exécuter dans le navigateur d'un autre est un problème majeur. Et c'est là que vous devez devenir vraiment réel.
akkishore

5
@akkishore, j'apprécierai si vous venez avec un exemple réel qui soutient vos déclarations sur-déclarées.
Akash Kava

7
@AkashKava Ce que vous ne réalisez pas, c'est que si je soumets javascript dans ma zone de commentaire, et que javascript arrive dans la base de données. Lorsqu'un autre utilisateur affiche ce commentaire (dans lequel j'ai mis du javascript), eval prendra ce javascript lors de son rendu et l'évaluera à l'aide de l'interpréteur, ce qui entraînera l'exécution de mon javascript intégré sur le navigateur de l'autre utilisateur. Ce faisant, je peux gleen toutes sortes d'informations. Leur nom d'utilisateur, leur identifiant d'utilisateur dans la base de données, leur adresse e-mail, etc. Ce n'est pas une réponse difficile, si vous aviez Googled XSS, vous verriez dans environ 10 secondes pourquoi c'est un problème.
Kyle Richter

18

J'ai tendance à suivre les conseils de Crockford pour eval()et éviter complètement. Même les moyens qui semblent l'exiger ne le font pas. Par exemple, le setTimeout()vous permet de passer une fonction plutôt que eval.

setTimeout(function() {
  alert('hi');
}, 1000);

Même s'il s'agit d'une source fiable , je ne l'utilise pas, car le code renvoyé par JSON peut être tronqué, ce qui pourrait au mieux faire quelque chose de chancelant, au pire, exposer quelque chose de mauvais.


2
Je pense que les bogues dans le formateur JSON côté serveur sont certainement un problème. La réponse du serveur dépend-elle de tout type de texte soumis par l'utilisateur? Ensuite, vous devez surveiller XSS.
swilliams

3
Si votre serveur Web n'est pas authentifié via HTTPS, vous pourriez subir une sorte d'attaque de l'homme du milieu où un autre hôte intercepte la demande et envoie ses propres données.
Ben Combee

11
Si quelqu'un peut effectuer une attaque d'homme au milieu, il peut facilement injecter n'importe quoi dans vos scripts.
el.pescado

10
Vous ne devriez pas vous fier du tout à votre code javascript ... Vous ne vous fiez pas à tout ce qui s'exécute côté client ... Si quelqu'un attaque l'homme au milieu, pourquoi jouerait-il avec vos objets json? Il peut vous servir une page Web différente et différents fichiers js ...
Calmarius

5
Personnellement, je n'aime pas l'argument "il y a toujours d'autres façons de le faire". Par exemple, vous pouvez également dire qu'il existe toujours des moyens d'éviter la programmation orientée objet. Cela ne signifie pas que ce n'est pas une excellente option. Si vous comprenez eval et ses dangers, il peut être un excellent outil à utiliser dans les bonnes situations.
dallin

4

J'ai vu des gens préconiser de ne pas utiliser eval, parce que c'est mal , mais j'ai vu les mêmes personnes utiliser Function et setTimeout dynamiquement, alors ils utilisent eval sous les hottes : D

BTW, si votre sandbox n'est pas assez sûr (par exemple, si vous travaillez sur un site qui autorise l'injection de code) eval est le dernier de vos problèmes. La règle de sécurité de base est que toutes les entrées sont mauvaises, mais dans le cas de JavaScript, même JavaScript lui-même peut être mauvais, car en JavaScript, vous pouvez remplacer n'importe quelle fonction et vous ne pouvez pas être sûr d'utiliser la vraie, donc, si un code malveillant démarre avant vous, vous ne pouvez faire confiance à aucune fonction intégrée JavaScript: D

Maintenant, l'épilogue de ce post est:

Si vous en avez VRAIMENT besoin (80% du temps, l'eval n'est PAS nécessaire) et que vous êtes sûr de ce que vous faites, utilisez simplement eval (ou une meilleure fonction;)), les fermetures et la POO couvrent les 80/90% des cas où eval peut être remplacé en utilisant un autre type de logique, le reste est du code généré dynamiquement (par exemple, si vous écrivez un interprète) et comme vous l'avez déjà dit évaluer JSON (ici vous pouvez utiliser l'évaluation sûre de Crockford;))


Et comme l'a souligné Crockford lui - même , les navigateurs Web actuels ont une fonction intégrée JSON.parse .
Ruud Helderman

4

Eval est complémentaire à la compilation qui est utilisée dans le modèle du code. Par modèle, je veux dire que vous écrivez un générateur de modèle simplifié qui génère un code de modèle utile qui augmente la vitesse de développement.

J'ai écrit un cadre, où les développeurs n'utilisent pas EVAL, mais ils utilisent notre cadre et à son tour ce cadre doit utiliser EVAL pour générer des modèles.

Les performances d'EVAL peuvent être améliorées en utilisant la méthode suivante; au lieu d'exécuter le script, vous devez renvoyer une fonction.

var a = eval("3 + 5");

Il devrait être organisé comme

var f = eval("(function(a,b) { return a + b; })");

var a = f(3,5);

La mise en cache f améliorera certainement la vitesse.

Chrome permet également de déboguer de telles fonctions très facilement.

En ce qui concerne la sécurité, utiliser eval ou non ne fera guère de différence,

  1. Tout d'abord, le navigateur invoque l'intégralité du script dans un bac à sable.
  2. Tout code qui est mauvais dans EVAL, est mauvais dans le navigateur lui-même. L'attaquant ou n'importe qui peut facilement injecter un nœud de script dans DOM et faire n'importe quoi s'il peut évaluer quoi que ce soit. Ne pas utiliser EVAL ne fera aucune différence.
  3. C'est surtout une mauvaise sécurité côté serveur qui est nuisible. Une mauvaise validation des cookies ou une mauvaise mise en œuvre de l'ACL sur le serveur provoque la plupart des attaques.
  4. Une récente vulnérabilité Java, etc. était présente dans le code natif de Java. JavaScript a été et est conçu pour s'exécuter dans un bac à sable, tandis que les applets ont été conçues pour s'exécuter en dehors d'un bac à sable avec des certificats, etc., qui entraînent des vulnérabilités et bien d'autres choses.
  5. Écrire du code pour imiter un navigateur n'est pas difficile. Tout ce que vous avez à faire est de faire une requête HTTP au serveur avec votre chaîne d'agent utilisateur préférée. De toute façon, tous les outils de test se moquent des navigateurs; si un attaquant veut vous faire du mal, EVAL est leur dernier recours. Ils ont de nombreuses autres façons de gérer votre sécurité côté serveur.
  6. Le navigateur DOM n'a pas accès aux fichiers et pas à un nom d'utilisateur. En fait, rien sur la machine auquel eval ne peut donner accès.

Si votre sécurité côté serveur est suffisamment solide pour que quiconque puisse attaquer de n'importe où, vous ne devriez pas vous soucier d'EVAL. Comme je l'ai mentionné, si EVAL n'existait pas, les attaquants ont de nombreux outils pour pirater votre serveur indépendamment de la capacité EVAL de votre navigateur.

Eval ne sert qu'à générer des modèles pour effectuer un traitement de chaîne complexe basé sur quelque chose qui n'est pas utilisé à l'avance. Par exemple, je préférerai

"FirstName + ' ' + LastName"

Par opposition à

"LastName + ' ' + FirstName"

Comme mon nom d'affichage, qui peut provenir d'une base de données et qui n'est pas codé en dur.


Vous pouvez utiliser la fonction au lieu de eval - function (first, last) { return last + ' ' + first }.
Konrad Borowski

Les noms des colonnes proviennent de la base de données.
Akash Kava

3
La menace evalest principalement d' autres utilisateurs . Supposons que vous ayez une page de paramètres et qu'elle vous permette de définir la façon dont votre nom apparaîtra aux autres. Disons également que vous ne pensiez pas très clairement lorsque vous l'avez écrit, donc votre boîte de sélection a des options comme <option value="LastName + ' ' + FirstName">Last First</option>. J'ouvre mes outils de développement, modifie l' valueoption d'une alert('PWNED!'), sélectionne l'option modifiée et envoie le formulaire. Maintenant, chaque fois qu'une autre personne peut voir mon nom d'affichage, ce code s'exécute.
cHao

@cHao, celui dont vous parlez est un exemple de mauvaise sécurité côté serveur, le serveur ne doit jamais accepter de données qui peuvent être exécutées en tant que code dans le navigateur de quiconque. Encore une fois, vous n'avez pas compris le concept d'une mauvaise sécurité côté serveur.
Akash Kava

1
Vous pouvez vous plaindre de la sécurité côté serveur si vous le souhaitez, mais le but evalest d'exécuter du code qui ne fait pas partie du script que vous avez écrit. Si vous n'avez pas besoin de pouvoir pour le faire (et vous ne le faites presque jamais), éviter evalaide à éviter toute une catégorie de problèmes. C'est une bonne chose si votre code côté serveur n'est pas parfait.
cHao

4

Lors du débogage dans Chrome (v28.0.1500.72), j'ai constaté que les variables ne sont pas liées aux fermetures si elles ne sont pas utilisées dans une fonction imbriquée qui produit la fermeture. Je suppose que c'est une optimisation du moteur JavaScript.

MAIS : quand eval()est utilisé à l'intérieur d'une fonction qui provoque une fermeture, TOUTES les variables des fonctions externes sont liées à la fermeture, même si elles ne sont pas utilisées du tout. Si quelqu'un a le temps de tester si des fuites de mémoire peuvent en résulter, laissez-moi un commentaire ci-dessous.

Voici mon code de test:

(function () {
    var eval = function (arg) {
    };

    function evalTest() {
        var used = "used";
        var unused = "not used";

        (function () {
            used.toString();   // Variable "unused" is visible in debugger
            eval("1");
        })();
    }

    evalTest();
})();

(function () {
    var eval = function (arg) {
    };

    function evalTest() {
        var used = "used";
        var unused = "not used";

        (function () {
            used.toString();   // Variable "unused" is NOT visible in debugger
            var noval = eval;
            noval("1");
        })();
    }

    evalTest();
})();

(function () {
    var noval = function (arg) {
    };

    function evalTest() {
        var used = "used";
        var unused = "not used";

        (function () {
            used.toString();    // Variable "unused" is NOT visible in debugger
            noval("1");
        })();
    }

    evalTest();
})();

Ce que j'aimerais souligner ici, c'est que eval () ne doit pas nécessairement faire référence à la eval()fonction native . Tout dépend du nom de la fonction . Ainsi, lors de l'appel du natif eval()avec un nom d'alias (disons var noval = eval;puis dans une fonction interne noval(expression);), l'évaluation de expressionpeut échouer lorsqu'elle fait référence à des variables qui devraient faire partie de la fermeture, mais ce n'est pas le cas.



3

Bottom Line

Si vous avez créé ou aseptisé le code vous eval, ce n'est jamais mal .

Un peu plus détaillé

evalest mauvais s'il s'exécute sur le serveur à l'aide d'une entrée soumise par un client qui n'a pas été créée par le développeur ou qui n'a pas été filtrée par le développeur .

evaln'est pas mauvais s'il s'exécute sur le client, même si vous utilisez une entrée non stérilisée conçue par le client .

Il est évident que vous devez toujours aseptiser l'entrée, d'avoir un certain contrôle sur ce que votre consume de code.

Raisonnement

Le client peut exécuter n'importe quel code arbitraire qu'il souhaite, même si le développeur ne l'a pas codé; Cela est vrai non seulement pour ce qui est évalué, mais pour l' appel à evallui-même .


2

La seule instance où vous devriez utiliser eval () est lorsque vous devez exécuter JS dynamique à la volée. Je parle de JS que vous téléchargez de manière asynchrone depuis le serveur ...

... Et 9 fois sur 10, vous pourriez facilement éviter de le faire en refactorisant.


De nos jours, il existe d'autres (et meilleures) façons de charger JavaScript de manière asynchrone à partir du serveur: w3bits.com/async-javascript
Ruud Helderman

1

evalest rarement le bon choix. Bien qu'il puisse y avoir de nombreux cas où vous pouvez accomplir ce que vous devez accomplir en concaténant un script ensemble et en l'exécutant à la volée, vous avez généralement des techniques beaucoup plus puissantes et maintenables à votre disposition: notation de tableau associatif ( obj["prop"]est la même que obj.prop) , fermetures, techniques orientées objet, techniques fonctionnelles - utilisez-les à la place.


1

En ce qui concerne le script client, je pense que la question de la sécurité est un point discutable. Tout ce qui est chargé dans le navigateur est sujet à manipulation et doit être traité comme tel. Il n'y a aucun risque à utiliser une instruction eval () lorsqu'il existe des moyens beaucoup plus faciles d'exécuter du code JavaScript et / ou de manipuler des objets dans le DOM, comme la barre d'URL dans votre navigateur.

javascript:alert("hello");

Si quelqu'un veut manipuler son DOM, je dis swing. La sécurité pour empêcher tout type d'attaque devrait toujours être la responsabilité de l'application serveur, point final.

D'un point de vue pragmatique, il n'y a aucun avantage à utiliser un eval () dans une situation où les choses peuvent être faites autrement. Cependant, il existe des cas spécifiques où un eval DEVRAIT être utilisé. Dans ce cas, cela peut certainement être fait sans risque de faire exploser la page.

<html>
    <body>
        <textarea id="output"></textarea><br/>
        <input type="text" id="input" />
        <button id="button" onclick="execute()">eval</button>

        <script type="text/javascript">
            var execute = function(){
                var inputEl = document.getElementById('input');
                var toEval = inputEl.value;
                var outputEl = document.getElementById('output');
                var output = "";

                try {
                    output = eval(toEval);
                }
                catch(err){
                    for(var key in err){
                        output += key + ": " + err[key] + "\r\n";
                    }
                }
                outputEl.value = output;
            }
        </script>
    <body>
</html>

6
Re "Il n'y a aucun risque à utiliser une instruction eval () quand il existe des moyens beaucoup plus faciles d'exécuter du javascript et / ou de manipuler des objets dans le DOM". L'injection de code est un problème lorsqu'un utilisateur peut entrer du code qui est ensuite exécuté dans le navigateur d'un autre utilisateur. Les consoles de navigateur ne permettent pas en elles-mêmes à un utilisateur d'exécuter du code dans le navigateur d'un autre utilisateur, elles ne sont donc pas pertinentes pour décider s'il vaut la peine de se protéger contre l'injection de code.
Mike Samuel

N'est pas <head></head>requis, même s'il est vide?
Peter Mortensen

2
Cette réponse ignore complètement les risques de XSS .
Ruud Helderman

1

Côté serveur, l'eval est utile lorsqu'il s'agit de scripts externes tels que sql ou influxdb ou mongo. Où la validation personnalisée au moment de l'exécution peut être effectuée sans redéployer vos services.

Par exemple un service de réalisation avec les métadonnées suivantes

{
  "568ff113-abcd-f123-84c5-871fe2007cf0": {
    "msg_enum": "quest/registration",
    "timely": "all_times",
    "scope": [
      "quest/daily-active"
    ],
    "query": "`SELECT COUNT(point) AS valid from \"${userId}/dump/quest/daily-active\" LIMIT 1`",
    "validator": "valid > 0",
    "reward_external": "ewallet",
    "reward_external_payload": "`{\"token\": \"${token}\", \"userId\": \"${userId}\", \"amountIn\": 1, \"conversionType\": \"quest/registration:silver\", \"exchangeProvider\":\"provider/achievement\",\"exchangeType\":\"payment/quest/registration\"}`"
  },
  "efdfb506-1234-abcd-9d4a-7d624c564332": {
    "msg_enum": "quest/daily-active",
    "timely": "daily",
    "scope": [
      "quest/daily-active"
    ],
    "query": "`SELECT COUNT(point) AS valid from \"${userId}/dump/quest/daily-active\" WHERE time >= '${today}' ${ENV.DAILY_OFFSET} LIMIT 1`",
    "validator": "valid > 0",
    "reward_external": "ewallet",
    "reward_external_payload": "`{\"token\": \"${token}\", \"userId\": \"${userId}\", \"amountIn\": 1, \"conversionType\": \"quest/daily-active:silver\", \"exchangeProvider\":\"provider/achievement\",\"exchangeType\":\"payment/quest/daily-active\"}`"
  }
}

Qui permettent alors,

  • Injection directe d'objets / valeurs via une chaîne littérale dans un json, utile pour les modèles de texte

  • Peut être utilisé comme comparateur, disons que nous établissons des règles pour valider une quête ou des événements dans le CMS

Contre cela:

  • Peut être des erreurs dans le code et casser des choses dans le service, s'il n'est pas entièrement testé.

  • Si un pirate peut écrire un script sur votre système, alors vous êtes à peu près foutu.

  • Une façon de valider votre script est de conserver le hachage de vos scripts dans un endroit sûr, afin que vous puissiez les vérifier avant de l'exécuter.


Agréable. Quand j'ai posé la question, je n'avais même pas pensé au JS côté serveur.
Richard Turner

1

Je pense que tout cas de valorisation de l'évaluation serait rare. Vous êtes plus susceptible de l'utiliser en pensant qu'il est justifié que vous ne l'utilisez lorsqu'il est réellement justifié.

Les problèmes de sécurité sont les plus connus. Mais sachez également que JavaScript utilise la compilation JIT et cela fonctionne très mal avec eval. Eval est un peu comme une boîte noire pour le compilateur, et JavaScript doit être en mesure de prédire le code à l'avance (dans une certaine mesure) afin d'appliquer en toute sécurité et correctement les optimisations de performances et la portée. Dans certains cas, l'impact sur les performances peut même affecter d'autres codes en dehors d'eval.

Si vous voulez en savoir plus: https://github.com/getify/You-Dont-Know-JS/blob/master/scope%20%26%20closures/ch2.md#eval


0

Vous pouvez l'utiliser si vous avez un contrôle total sur le code transmis à la evalfonction.


2
Si vous avez un contrôle complet sur ce à quoi vous passez eval, la grande question devient alors: quand est-il logique que ce soit une chaîne plutôt qu'un vrai JS?
cHao

@cHao Par exemple, si vous avez une grande application de jeu (5-10 Mo Javascript), il vaut mieux construire d'abord un simple AJAX-Preloader à chargement rapide (1 Ko), qui charge le grand script principal, tout en affichant un chargement- Bar ou quelque chose de similaire. Après le téléchargement, vous pouvez utiliser "eval (source)" ou mieux "nouvelle fonction (source)" pour exécuter le Game-Application-Script chargé. De cette façon, l'utilisateur peut voir visuellement que l'application a besoin de temps pour télécharger jusqu'au démarrage du jeu. Sans cela, l'utilisateur doit attendre que l'application entière se charge sans aucun retour visuel.
SammieFox

@SammieFox Il existe d'autres (et meilleures) façons de le faire, notamment <script async="true" src="...">. Voir aussi: w3bits.com/async-javascript
Ruud Helderman

La réponse est un conseil dangereux; trop de développeurs ont un faux sentiment de contrôle. Le conseil fait faire un certain sens pour les logiciels qui ne sont plus activement maintenu. Mais un tel logiciel doit être considéré comme mort.
Ruud Helderman

0

Uniquement pendant les tests, si possible. Notez également que eval () est beaucoup plus lent que les autres évaluateurs JSON spécialisés, etc.


0

Il n'y a aucune raison de ne pas utiliser eval () tant que vous pouvez être sûr que la source du code provient de vous ou de l'utilisateur réel. Même s'il peut manipuler ce qui est envoyé dans la fonction eval (), ce n'est pas un problème de sécurité, car il est capable de manipuler le code source du site Web et pourrait donc changer le code JavaScript lui-même.

Alors ... quand ne pas utiliser eval ()? Eval () ne doit pas être utilisé uniquement lorsqu'il existe un risque qu'un tiers le modifie. Comme intercepter la connexion entre le client et votre serveur (mais si c'est un problème, utilisez HTTPS). Vous ne devriez pas utiliser eval () pour analyser du code écrit par d'autres comme dans un forum.


Re "Il n'y a aucune raison de ne pas utiliser eval () tant que vous pouvez être sûr que la source du code vient de vous ou de l'utilisateur réel." Cela suppose qu'il existe un seul utilisateur. Cette prémisse n'est pas énoncée dans le PO. Lorsqu'il y a plusieurs utilisateurs, la négligence evald'une chaîne composée du contenu d'un utilisateur peut permettre à cet utilisateur d'exécuter du code dans le navigateur de l'autre utilisateur.
Mike Samuel

@MikeSamuel, eval peut exécuter du code dans le navigateur d'un autre utilisateur, je n'ai pas entendu cela, avez-vous essayé cela? Cela ne s'est jamais produit dans l'histoire de la navigation, pouvez-vous nous en montrer un exemple?
Akash Kava

@AkashKava, Une chaîne peut provenir d'un agent utilisateur, être stockée dans une base de données, puis servie à un autre navigateur qui la constitue eval. Cela arrive tout le temps.
Mike Samuel

Base de données @MikeSamuel? où? qui sert une fausse chaîne? n'est-ce pas la base de données côté serveur à blâmer? tout d'abord EVAL n'est pas à blâmer pour le code côté serveur mal écrit. Utilisez jsfiddle et montrez au monde un exemple du monde réel où il peut nuire.
Akash Kava

2
@AkashKava, je ne comprends pas votre question. Nous ne parlons pas d'une application spécifique, mais des raisons de ne pas l'utiliser eval. Comment est-il utile de blâmer le serveur? Si quelqu'un devait être blâmé, ce devrait être l'attaquant. Indépendamment du blâme, un client qui n'est pas vulnérable à XSS malgré les bogues du serveur est meilleur qu'un client qui est vulnérable, toutes choses étant égales par ailleurs.
Mike Samuel

0

Si c'est vraiment nécessaire, l'eval n'est pas mal. Mais 99,9% des utilisations d'eval que je rencontre ne sont pas nécessaires (à l'exclusion des éléments setTimeout).

Pour moi, le mal n'est pas une performance ou même un problème de sécurité (enfin, indirectement c'est les deux). Toutes ces utilisations inutiles d'eval s'ajoutent à un enfer de maintenance. Les outils de refactoring sont supprimés. La recherche de code est difficile. Les effets imprévus de ces événements sont légion.


5
eval n'est pas nécessaire pour setTimeout. Vous pouvez également utiliser une référence de fonction.
Matthew Crumley

0

Quand eval () de JavaScript n'est-il pas mal?

J'essaie toujours de me décourager d'utiliser eval . Presque toujours, une solution plus propre et plus facile à entretenir est disponible. Eval n'est pas nécessaire, même pour l'analyse JSON . Eval ajoute à l'enfer de la maintenance . Non sans raison, il est mal vu par des maîtres comme Douglas Crockford.

Mais j'ai trouvé un exemple où il devrait être utilisé:

Lorsque vous devez transmettre l'expression.

Par exemple, j'ai une fonction qui construit un google.maps.ImageMapTypeobjet général pour moi, mais je dois lui dire la recette, comment devrait-elle construire l'URL de la tuile à partir des paramètres zoomet coord:

my_func({
    name: "OSM",
    tileURLexpr: '"http://tile.openstreetmap.org/"+b+"/"+a.x+"/"+a.y+".png"',
    ...
});

function my_func(opts)
{
    return new google.maps.ImageMapType({
        getTileUrl: function (coord, zoom) {
            var b = zoom;
            var a = coord;
            return eval(opts.tileURLexpr);
        },
        ....
    });
}

3
On dirait qu'il pourrait être refactorisé pour que eval () ne soit pas nécessaire - tileURLexpr n'est qu'un modèle afin qu'une utilisation judicieuse de replace () fasse le travail. Pourtant, cela me rappelle un exemple que j'avais en tête lorsque j'ai soumis la question, qui était de permettre à un utilisateur de spécifier une formule mathématique à évaluer, similaire à la fonctionnalité de feuille de calcul. Bien sûr, je ne l'ai pas mentionné à l'époque car je ne voulais pas influencer les réponses!
Richard Turner

8
tileURL: function (zoom, coord) { return 'http://tile.openstreetmap.org/' + b + '/' + a.x + '/' + a.y + '.png'; },
Casey Chu

0

Mon exemple d'utilisation eval: import .

Comment cela se fait habituellement.

var components = require('components');
var Button = components.Button;
var ComboBox = components.ComboBox;
var CheckBox = components.CheckBox;
...
// That quickly gets very boring

Mais avec l'aide de evalet une petite fonction d'aide, cela devient beaucoup mieux:

var components = require('components');
eval(importable('components', 'Button', 'ComboBox', 'CheckBox', ...));

importable pourrait ressembler (cette version ne prend pas en charge l'importation de membres concrets).

function importable(path) {
    var name;
    var pkg = eval(path);
    var result = '\n';

    for (name in pkg) {
        result += 'if (name !== undefined) throw "import error: name already exists";\n'.replace(/name/g, name);
    }

    for (name in pkg) {
        result += 'var name = path.name;\n'.replace(/name/g, name).replace('path', path);
    }
    return result;
}

2
+1 pour l'idée, mais vous avez un bug ici: .replace(/name/g, name).replace('path', path). Si namecontient la chaîne, "path"vous pourriez obtenir des surprises.
wberry

1
Déclarer une variable pour chaque propriété de componentsest une odeur de code possible; la refactorisation de votre code pourrait éliminer complètement le «problème». Votre solution actuelle n'est que du sucre syntaxique. Si vous insistez pour le faire, alors je recommanderais d'écrire votre propre préprocesseur, à exécuter avant le déploiement. Cela devrait rester à l' evalécart du code de production.
Ruud Helderman

0

Eval n'est pas mal, juste mal utilisé.

Si vous avez créé le code qui s'y trouve ou pouvez y faire confiance, ça va. Les gens n'arrêtent pas de dire que la saisie des utilisateurs n'a pas d'importance avec eval. Eh bien, en quelque sorte ~

S'il y a une entrée utilisateur qui va au serveur, puis revient au client, et ce code est utilisé dans eval sans être filtré. Félicitations, vous avez ouvert la boîte de Pandore pour que les données des utilisateurs soient envoyées à quiconque.

Selon l'emplacement de l'évaluation, de nombreux sites Web utilisent des SPA, et l'évaluation pourrait faciliter l'accès des utilisateurs aux applications internes, ce qui n'aurait pas été facile autrement. Maintenant, ils peuvent créer une extension de navigateur bidon qui peut enregistrer cette évaluation et voler à nouveau des données.

Je dois juste comprendre quel est l'intérêt de vous en utilisant l'eval. La génération de code n'est pas vraiment idéale quand vous pouvez simplement créer des méthodes pour faire ce genre de choses, utiliser des objets ou similaires.

Maintenant, un bel exemple d'utilisation d'eval. Votre serveur lit le fichier swagger que vous avez créé. De nombreux paramètres d'URL sont créés au format {myParam}. Vous souhaitez donc lire les URL, puis les convertir en chaînes de modèle sans avoir à effectuer des remplacements complexes, car vous avez de nombreux points de terminaison. Vous pouvez donc faire quelque chose comme ça. Notez qu'il s'agit d'un exemple très simple.

const params = { id: 5 };

const route = '/api/user/{id}';
route.replace(/{/g, '${params.');

// use eval(route); to do something

-1

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 . 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({}))

Les performances 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 .


"L'hyperscript est d'abord généré sous forme de chaîne (...)" Il est plus logique de faire toute la génération de code dans la phase de construction, d'écrire le code d'hyperscript résultant dans un fichier exécutable distinct (.js), puis de déployer ce fichier pour tester et production. J'adore la façon dont vous utilisez la génération de code. C'est juste que evalc'est un indice qu'une responsabilité qui appartient au moment de la compilation est passée à l'exécution.
Ruud Helderman

-1

Ma conviction est que eval est une fonction très puissante pour les applications Web côté client et sûre ... Aussi sûre que JavaScript, ce qui n'est pas le cas. :-) Les problèmes de sécurité sont essentiellement un problème côté serveur car, maintenant, avec des outils comme Firebug, vous pouvez attaquer n'importe quelle application JavaScript.


1
L'utilisation de evaldoit être sécurisée contre les attaques XSS, ce qui n'est pas toujours facile à bien faire.
Benjamin

-1

Eval est utile pour la génération de code lorsque vous n'avez pas de macros.

Par exemple (stupide), si vous écrivez un compilateur Brainfuck , vous voudrez probablement construire une fonction qui exécute la séquence d'instructions sous forme de chaîne, et l'évaluer pour renvoyer une fonction.


Soit vous écrivez un compilateur (qui enregistre au lieu d'exécuter le code généré) soit vous écrivez un interpréteur (où chaque instruction a une implémentation précompilée). Ce n'est pas non plus un cas d'utilisation pour eval.
Ruud Helderman

Si vous avez généré du code javascript et que vous souhaitez l'exécuter immédiatement (disons pour des avantages de performance par rapport à l'interprétation directe), ce serait un cas d'utilisation pour eval.
Erik Haliewicz

Bon point; J'ai vu un exemple dans cet article sur Blockly . Je suis choqué par Google eval, lorsque l'alternative ( Fonction ) est à la fois plus rapide ( comme expliqué dans MDN ) et plus fiable (empêche les bogues imprévisibles par une meilleure isolation entre le code généré et d'autres codes `` de support '' sur la même page Web).
Ruud Helderman

-5

Lorsque vous analysez une structure JSON avec une fonction d'analyse (par exemple, jQuery.parseJSON), elle attend une structure parfaite du fichier JSON (chaque nom de propriété est entre guillemets). Cependant, JavaScript est plus flexible. Par conséquent, vous pouvez utiliser eval () pour l'éviter.


4
Ne pas utiliser aveuglément eval, en particulier. lors de l'obtention de données JSON à partir d'une source tierce. Voir JSON.Stringify sans guillemets sur les propriétés? pour la bonne approche pour analyser "JSON sans noms de clés entre guillemets".
Rob W

2
S'il n'utilise pas de guillemets doubles autour des noms de propriété, il peut s'agir d'une représentation sous forme de chaîne d'un littéral d'objet, mais il ne s'agit pas de JSON . JSON définit les noms de propriété comme un stringet définit a stringcomme une séquence de zéro ou plusieurs caractères Unicode, entourés de guillemets doubles, en utilisant des échappements antislash.
Code inutile

Voir l'article de Nikolas Zakas - "eval () n'est pas mal, juste mal compris" nczonline.net/blog/2013/06/25/eval-isnt-evil-just-misunderstood
vitmalina

@vitmalina Extrait de l'article de Zakas: "Cela peut être dangereux si vous prenez les entrées des utilisateurs et les exécutez via eval (). Cependant, si vos entrées ne proviennent pas de l'utilisateur, y a-t-il un réel danger?" Voilà exactement le problème. Une fois que votre code dépasse les proportions du `` bonjour '', il devient rapidement impossible de prouver que vous ne divulguez pas les entrées des utilisateurs eval. Dans toute application Web sérieuse multi-locataire, avec des dizaines de développeurs travaillant sur la même base de code, cela est inacceptable.
Ruud Helderman
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.