Die Auswertungsfunktion ist eine leistungsstarke und einfache Möglichkeit, Code dynamisch zu generieren. Was sind also die Einschränkungen?
Die Auswertungsfunktion ist eine leistungsstarke und einfache Möglichkeit, Code dynamisch zu generieren. Was sind also die Einschränkungen?
Antworten:
Die unsachgemäße Verwendung von eval öffnet Ihren Code für Injektionsangriffe
Das Debuggen kann schwieriger sein (keine Zeilennummern usw.)
eval'd Code wird langsamer ausgeführt (keine Möglichkeit, eval'd Code zu kompilieren / zwischenzuspeichern)
Bearbeiten: Wie @Jeff Walden in den Kommentaren hervorhebt, ist # 3 heute weniger wahr als 2008. Obwohl einige kompilierte Skripte zwischengespeichert werden können, ist dies nur auf Skripte beschränkt, die ohne Änderung wiederholt werden. Ein wahrscheinlicheres Szenario ist, dass Sie Skripte bewerten, die jedes Mal geringfügig geändert wurden und daher nicht zwischengespeichert werden konnten. Sagen wir einfach, dass EINIGER ausgewerteter Code langsamer ausgeführt wird.
badHackerGuy'); doMaliciousThings();
Folgendes setzen: Wenn Sie meinen Benutzernamen in ein Skript einbinden und ihn in den Browsern anderer Leute auswerten, kann ich jedes gewünschte Javascript auf ihren Computern ausführen (z. B. sie zwingen, +1 meiner Beiträge zu senden,
debugger;
Anweisung zu Ihrem Quellcode hinzufügen . Dadurch wird die Ausführung Ihres Programms in dieser Zeile gestoppt. Danach können Sie Debug-Haltepunkte hinzufügen, als wäre es nur eine weitere JS-Datei.
eval ist nicht immer böse. Es gibt Zeiten, in denen es vollkommen angemessen ist.
Allerdings wird eval derzeit und historisch von Menschen, die nicht wissen, was sie tun, massiv überstrapaziert. Dazu gehören leider auch Leute, die JavaScript-Tutorials schreiben, und in einigen Fällen kann dies tatsächlich Sicherheitsfolgen haben - oder häufiger einfache Fehler. Je mehr wir tun können, um ein Fragezeichen über die Bewertung zu werfen, desto besser. Jedes Mal, wenn Sie eval verwenden, müssen Sie überprüfen, was Sie tun, da Sie es wahrscheinlich besser, sicherer und sauberer machen könnten.
Um ein allzu typisches Beispiel zu geben, um die Farbe eines Elements mit einer ID festzulegen, die in der Variablen 'Kartoffel' gespeichert ist:
eval('document.' + potato + '.style.color = "red"');
Wenn die Autoren der oben genannten Art von Code eine Ahnung von den Grundlagen der Funktionsweise von JavaScript-Objekten hätten, hätten sie erkannt, dass eckige Klammern anstelle von wörtlichen Punktnamen verwendet werden können, sodass keine Bewertung erforderlich ist:
document[potato].style.color = 'red';
... was viel einfacher zu lesen und weniger potenziell fehlerhaft ist.
(Aber dann würde jemand, der / wirklich / wusste, was er tat, sagen:
document.getElementById(potato).style.color = 'red';
Das ist zuverlässiger als der zwielichtige alte Trick, direkt aus dem Dokumentobjekt heraus auf DOM-Elemente zuzugreifen.)
JSON.parse()
statt eval()
für JSON verwenden?
Ich glaube, das liegt daran, dass es jede JavaScript-Funktion aus einem String ausführen kann. Die Verwendung erleichtert es Benutzern, unerwünschten Code in die Anwendung einzufügen.
Zwei Punkte fallen mir ein:
Sicherheit (aber solange Sie die zu bewertende Zeichenfolge selbst generieren, ist dies möglicherweise kein Problem)
Leistung: Bis der auszuführende Code unbekannt ist, kann er nicht optimiert werden. (über Javascript und Performance, sicherlich Steve Yegges Präsentation )
eval
speichert und dieser kleine Code dann im B-Browser des Benutzers ausgeführt wird
Das Übergeben von Benutzereingaben an eval () ist ein Sicherheitsrisiko, aber auch jeder Aufruf von eval () erstellt eine neue Instanz des JavaScript-Interpreters. Dies kann ein Ressourcenfresser sein.
Hauptsächlich ist es viel schwieriger zu warten und zu debuggen. Es ist wie ein goto
. Sie können es verwenden, aber es macht es schwieriger, Probleme zu finden, und es erschwert die Personen, die möglicherweise später Änderungen vornehmen müssen.
Beachten Sie, dass Sie eval () häufig verwenden können, um Code in einer ansonsten eingeschränkten Umgebung auszuführen. Websites für soziale Netzwerke, die bestimmte JavaScript-Funktionen blockieren, können manchmal durch Aufteilen in einen eval-Block getäuscht werden.
eval('al' + 'er' + 't(\'' + 'hi there!' + '\')');
Wenn Sie also JavaScript-Code ausführen möchten , der sonst möglicherweise nicht zulässig ist ( Myspace , ich sehe Sie an ...), kann eval () ein nützlicher Trick sein.
Aus all den oben genannten Gründen sollten Sie es jedoch nicht für Ihren eigenen Code verwenden, bei dem Sie die vollständige Kontrolle haben - es ist einfach nicht erforderlich und besser in das Regal der kniffligen JavaScript-Hacks verbannt.
[]["con"+"struc"+"tor"]["con"+"struc"+"tor"]('al' + 'er' + 't(\'' + 'hi there!' + '\')')()
Sofern Sie eval () keinen dynamischen Inhalt (über CGI oder Eingabe) zulassen, ist er genauso sicher und solide wie alle anderen JavaScript-Elemente auf Ihrer Seite.
Zusammen mit den restlichen Antworten glaube ich nicht, dass eval-Aussagen eine erweiterte Minimierung haben können.
Dies ist ein mögliches Sicherheitsrisiko, hat einen anderen Ausführungsbereich und ist ziemlich ineffizient, da eine völlig neue Skriptumgebung für die Ausführung des Codes erstellt wird. Weitere Informationen finden Sie hier: eval .
Es ist jedoch sehr nützlich und kann mit Mäßigung viele gute Funktionen hinzufügen.
Wenn Sie nicht zu 100% sicher sind, dass der ausgewertete Code aus einer vertrauenswürdigen Quelle stammt (normalerweise Ihrer eigenen Anwendung), können Sie Ihr System auf todsichere Weise einem Cross-Site-Scripting-Angriff aussetzen.
Ich weiß, dass diese Diskussion alt ist, aber ich mag diesen Ansatz von Google wirklich und wollte dieses Gefühl mit anderen teilen;)
Die andere Sache ist, dass je besser du wirst, desto mehr versuchst du zu verstehen und schließlich glaubst du einfach nicht, dass etwas gut oder schlecht ist, nur weil es jemand gesagt hat :) Dies ist ein sehr inspirierendes Video , das mir geholfen hat, mehr selbst zu denken :) GUTE PRAXIS sind gut, aber benutze sie nicht sinnlos :)
Es ist nicht unbedingt so schlimm, vorausgesetzt Sie wissen, in welchem Kontext Sie es verwenden.
Wenn Ihre Anwendung verwendet eval()
, um ein Objekt aus einem JSON zu erstellen, das von einer XMLHttpRequest auf Ihre eigene Site zurückgekehrt ist und von Ihrem vertrauenswürdigen serverseitigen Code erstellt wurde, ist dies wahrscheinlich kein Problem.
Nicht vertrauenswürdiger clientseitiger JavaScript-Code kann sowieso nicht so viel. Vorausgesetzt, das, was Sie ausführen, eval()
stammt aus einer vernünftigen Quelle, geht es Ihnen gut.
Dies verringert Ihr Vertrauen in die Sicherheit erheblich.
Wenn Sie möchten, dass der Benutzer einige logische Funktionen eingibt und für UND das ODER auswertet, ist die JavaScript-Bewertungsfunktion perfekt. Ich kann zwei Zeichenfolgen und eval(uate) string1 === string2
usw. akzeptieren .
Wenn Sie die Verwendung von eval () in Ihrem Code entdecken, denken Sie an das Mantra „eval () ist böse“.
Diese Funktion nimmt eine beliebige Zeichenfolge und führt sie als JavaScript-Code aus. Wenn der betreffende Code im Voraus bekannt ist (zur Laufzeit nicht festgelegt), gibt es keinen Grund, eval () zu verwenden. Wenn der Code zur Laufzeit dynamisch generiert wird, gibt es oft einen besseren Weg, um das Ziel ohne eval () zu erreichen. Beispielsweise ist es besser und einfacher, nur die eckige Klammer für den Zugriff auf dynamische Eigenschaften zu verwenden:
// antipattern
var property = "name";
alert(eval("obj." + property));
// preferred
var property = "name";
alert(obj[property]);
Die Verwendung eval()
hat auch Auswirkungen auf die Sicherheit, da Sie möglicherweise Code ausführen (z. B. aus dem Netzwerk), der manipuliert wurde. Dies ist ein häufiges Antimuster, wenn eine JSON-Antwort von einer Ajax-Anforderung verarbeitet wird. In diesen Fällen ist es besser, die integrierten Methoden des Browsers zu verwenden, um die JSON-Antwort zu analysieren und sicherzustellen, dass sie sicher und gültig ist. Für Browser, die nicht JSON.parse()
nativ unterstützt werden, können Sie eine Bibliothek von JSON.org verwenden.
Es ist auch wichtig , dass die Weitergabe Saiten zu erinnern setInterval()
, setTimeout()
und der Function()
Konstruktor ist, zum größten Teil, ähnlich wie bei eval()
und sollte daher vermieden werden.
Hinter den Kulissen muss JavaScript die Zeichenfolge, die Sie als Programmcode übergeben, noch auswerten und ausführen:
// antipatterns
setTimeout("myFunc()", 1000);
setTimeout("myFunc(1, 2, 3)", 1000);
// preferred
setTimeout(myFunc, 1000);
setTimeout(function () {
myFunc(1, 2, 3);
}, 1000);
Die Verwendung des neuen Konstruktors Function () ähnelt eval () und sollte mit Vorsicht behandelt werden. Es könnte ein mächtiges Konstrukt sein, wird aber oft missbraucht. Wenn Sie unbedingt verwenden müssen eval()
, können Sie stattdessen die Funktion new Function () in Betracht ziehen.
Es gibt einen kleinen potenziellen Vorteil, da der in new Function () ausgewertete Code in einem lokalen Funktionsbereich ausgeführt wird, sodass Variablen, die im ausgewerteten Code mit var definiert sind, nicht automatisch global werden.
Eine andere Möglichkeit, automatische Globals zu verhindern, besteht darin, den eval()
Anruf in eine Sofortfunktion umzuwandeln
.
Neben den möglichen Sicherheitsproblemen, wenn Sie vom Benutzer übermittelten Code ausführen, gibt es meistens einen besseren Weg, bei dem der Code nicht jedes Mal neu analysiert wird, wenn er ausgeführt wird. Anonyme Funktionen oder Objekteigenschaften können die meisten Anwendungen von eval ersetzen und sind viel sicherer und schneller.
Dies kann zu einem größeren Problem werden, da die nächste Generation von Browsern die Variante eines JavaScript-Compilers aufweist. Über Eval ausgeführter Code funktioniert möglicherweise nicht so gut wie der Rest Ihres JavaScript für diese neueren Browser. Jemand sollte ein Profil erstellen.
Dies ist einer der guten Artikel über Eval und wie es kein Übel ist: http://www.nczonline.net/blog/2013/06/25/eval-isnt-evil-just-misunderstood/
Ich sage nicht, dass Sie ausgehen und eval () überall verwenden sollten. Tatsächlich gibt es nur sehr wenige gute Anwendungsfälle für die Ausführung von eval (). Es gibt definitiv Bedenken hinsichtlich der Klarheit des Codes, der Debugbarkeit und der Leistung, die nicht übersehen werden sollten. Aber Sie sollten keine Angst haben, es zu verwenden, wenn Sie einen Fall haben, in dem eval () Sinn macht. Versuchen Sie, es nicht zuerst zu verwenden, aber lassen Sie sich von niemandem erschrecken, wenn Sie glauben, Ihr Code sei anfälliger oder weniger sicher, wenn eval () ordnungsgemäß verwendet wird.
eval () ist sehr leistungsfähig und kann verwendet werden, um eine JS-Anweisung auszuführen oder einen Ausdruck auszuwerten. Bei der Frage geht es jedoch nicht um die Verwendung von eval (), sondern nur darum, wie die Zeichenfolge, die Sie mit eval () ausführen, von einer böswilligen Partei beeinflusst wird. Am Ende wird bösartiger Code ausgeführt. Mit der Macht geht eine große Verantwortung einher. Verwenden Sie es also mit Bedacht, wenn Sie es verwenden. Dies hat nicht viel mit der Funktion eval () zu tun, aber dieser Artikel enthält ziemlich gute Informationen: http://blogs.popart.com/2009/07/javascript-injection-attacks/ Wenn Sie nach den Grundlagen von eval () suchen Schauen Sie hier: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval
Die JavaScript Engine verfügt über eine Reihe von Leistungsoptimierungen, die während der Kompilierungsphase durchgeführt werden. Einige davon beschränken sich darauf, den Code während der Lexik im Wesentlichen statisch analysieren und vorab bestimmen zu können, wo sich alle Variablen- und Funktionsdeklarationen befinden, sodass das Auflösen von Bezeichnern während der Ausführung weniger Aufwand erfordert.
Wenn die Engine jedoch eine Auswertung (..) im Code findet, muss sie im Wesentlichen davon ausgehen, dass die gesamte Kenntnis der Position des Bezeichners ungültig ist, da sie zum Zeitpunkt der Lexierung nicht genau wissen kann, welchen Code Sie an eval (..) übergeben können. um den lexikalischen Bereich oder den Inhalt des Objekts zu ändern, an das Sie übergeben können, um einen neuen lexikalischen Bereich zu erstellen, der konsultiert werden soll.
Mit anderen Worten, im pessimistischen Sinne sind die meisten dieser Optimierungen sinnlos, wenn eval (..) vorhanden ist, sodass die Optimierungen einfach überhaupt nicht durchgeführt werden.
Das erklärt alles.
Referenz :
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
Das ist nicht immer eine schlechte Idee. Nehmen Sie zum Beispiel die Codegenerierung. Ich habe kürzlich eine Bibliothek namens Hyperbars geschrieben, die die Lücke zwischen Virtual-Dom und Lenker schließt . Dazu wird eine Lenkervorlage analysiert und in Hyperscript konvertiert, das anschließend von virtual-dom verwendet wird. Das Hyperskript wird zuerst als Zeichenfolge generiert und vor der Rückgabe eval()
in ausführbaren Code umgewandelt. Ich habe eval()
in dieser besonderen Situation das genaue Gegenteil des Bösen gefunden.
Grundsätzlich aus
<div>
{{#each names}}
<span>{{this}}</span>
{{/each}}
</div>
Dazu
(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({}))
Die Leistung von eval()
ist in einer solchen Situation kein Problem, da Sie die generierte Zeichenfolge nur einmal interpretieren und die ausführbare Ausgabe dann mehrmals wiederverwenden müssen.
Sie können sehen, wie die Codegenerierung erreicht wurde, wenn Sie neugierig sind .
Ich würde sogar sagen, dass es nicht wirklich wichtig ist, wenn Sie eval()
Javascript verwenden, das in Browsern ausgeführt wird. * (Einschränkung)
Alle modernen Browser verfügen über eine Entwicklerkonsole, in der Sie ohnehin beliebiges Javascript ausführen können, und jeder semi-intelligente Entwickler kann sich Ihre JS-Quelle ansehen und alles, was er benötigt, in die Entwicklungskonsole einfügen, um das zu tun, was er möchte.
* Solange Ihre Serverendpunkte die korrekte Validierung und Bereinigung der vom Benutzer angegebenen Werte aufweisen, sollte es keine Rolle spielen, was in Ihrem clientseitigen Javascript analysiert und ausgewertet wird.
Wenn Sie jedoch fragen, ob es für die Verwendung eval()
in PHP geeignet ist , lautet die Antwort NEIN , es sei denn, Sie führen Werte auf die Whitelist , die möglicherweise an Ihre Bewertung übergeben werden.
Ich werde nicht versuchen, etwas zu widerlegen, was bisher gesagt wurde, aber ich werde diese Verwendung von eval () anbieten, die (soweit ich weiß) nicht anders gemacht werden kann. Es gibt wahrscheinlich andere Möglichkeiten, dies zu codieren und wahrscheinlich zu optimieren, aber dies geschieht aus Gründen der Klarheit lange und ohne Schnickschnack, um die Verwendung von eval zu veranschaulichen, für die es wirklich keine anderen Alternativen gibt. Das heißt: dynamische (oder genauer) programmgesteuert erstellte Objektnamen (im Gegensatz zu Werten).
//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);
BEARBEITEN: Übrigens würde ich (aus allen bisher genannten Sicherheitsgründen) nicht vorschlagen, dass Sie Ihre Objektnamen auf Benutzereingaben basieren. Ich kann mir keinen guten Grund vorstellen, warum Sie das tun möchten. Trotzdem dachte ich, ich würde darauf hinweisen, dass es keine gute Idee wäre :)
namespaceToTest[namespaceParts[i]]
, ohne dass hier eine if(typeof namespaceToTest[namespaceParts[i]] === 'undefined') { namespaceToTest[namespaceParts[i]] = {};
else namespaceToTest = namespaceToTest[namespaceParts[i]];
Müllabfuhr
Die Garbage Collection des Browsers hat keine Ahnung, ob der ausgewertete Code aus dem Speicher entfernt werden kann, sodass er nur gespeichert bleibt, bis die Seite neu geladen wird. Nicht schlecht, wenn Ihre Benutzer nur in Kürze auf Ihrer Seite sind, aber es kann ein Problem für Webanwendungen sein.
Hier ist ein Skript, um das Problem zu demonstrieren
https://jsfiddle.net/CynderRnAsh/qux1osnw/
document.getElementById("evalLeak").onclick = (e) => {
for(let x = 0; x < 100; x++) {
eval(x.toString());
}
};
Etwas so Einfaches wie der obige Code bewirkt, dass eine kleine Menge Speicher gespeichert wird, bis die App stirbt. Dies ist schlimmer, wenn das ausgewertete Skript eine Riesenfunktion ist und Intervall aufgerufen wird.