Wann ist eval () von JavaScript nicht böse?

263

Ich schreibe JavaScript-Code, um vom Benutzer eingegebene Funktionen zu analysieren (für Tabellenkalkulationsfunktionen). Nachdem ich die Formel analysiert hatte, konnte ich sie in JavaScript konvertieren und darauf ausführen eval(), um das Ergebnis zu erhalten.

Ich habe mich jedoch immer davor gescheut, es zu verwenden, eval()wenn ich es vermeiden kann, weil es böse ist (und zu Recht oder zu Unrecht habe ich immer gedacht, dass es in JavaScript noch böser ist, weil der zu bewertende Code vom Benutzer möglicherweise geändert wird ).

Also, wann ist es in Ordnung, es zu benutzen?

Richard Turner
quelle
5
Die meisten JSON-Bibliotheken verwenden eval under the Hood nicht genau zum Schutz vor Sicherheitsrisiken.
Sean McMillan
11
@ Sean - Sowohl JQuery als auch Prototype verwenden eval (JQuery verwendet es über die neue Funktion)
plodder
5
@plodder - Woher bekommst du deine Infos? jQuery verwendet seit 1.4 die native JSON.parse () (bereits im Jahr 1/2010)! Überzeugen Sie sich
Ken
3
"Offensichtlich muss man eval () verwenden, um JSON zu analysieren" - das ist im Gegenteil nicht wahr - man sollte eval nicht verwenden, um JSON zu analysieren! Verwenden Sie das json2.js-Skript von Douglas Crockfords (Ersteller von JSON) von json.org !
TMS
11
@Tomas die Ironie, dass json2.js eval verwendet, um JSON zu analysieren
tobyodavies

Antworten:

262

Ich möchte mir einen Moment Zeit nehmen, um die Prämisse Ihrer Frage anzusprechen - dass eval () " böse " ist. Das Wort " böse ", wie es von Programmiersprachenmenschen verwendet wird, bedeutet normalerweise "gefährlich" oder genauer "in der Lage, mit einem einfach aussehenden Befehl viel Schaden anzurichten". Wann ist es in Ordnung, etwas Gefährliches zu verwenden? Wenn Sie wissen, was die Gefahr ist, und wenn Sie die entsprechenden Vorsichtsmaßnahmen treffen.

Schauen wir uns die Gefahren bei der Verwendung von eval () an. Es gibt wahrscheinlich viele kleine versteckte Gefahren, genau wie alles andere, aber die beiden großen Risiken - der Grund, warum eval () als böse angesehen wird - sind Leistung und Code-Injection.

  • Performance - eval () führt den Interpreter / Compiler aus. Wenn Ihr Code kompiliert ist, ist dies ein großer Erfolg, da Sie mitten in der Laufzeit einen möglicherweise schweren Compiler aufrufen müssen. JavaScript ist jedoch immer noch meistens eine interpretierte Sprache, was bedeutet, dass das Aufrufen von eval () im allgemeinen Fall kein großer Leistungseinbruch ist (siehe jedoch meine spezifischen Anmerkungen unten).
  • Code-Injection - eval () führt möglicherweise eine Code-Zeichenfolge unter erhöhten Berechtigungen aus. Beispielsweise möchte ein Programm, das als Administrator / Root ausgeführt wird, niemals Benutzereingaben auswerten (), da diese Eingabe möglicherweise "rm -rf / etc / important-file" oder schlechter sein könnte. Auch hier hat JavaScript in einem Browser dieses Problem nicht, da das Programm ohnehin im eigenen Konto des Benutzers ausgeführt wird. Serverseitiges JavaScript könnte dieses Problem haben.

Weiter zu Ihrem speziellen Fall. Soweit ich weiß, generieren Sie die Zeichenfolgen selbst. Wenn Sie also darauf achten, dass keine Zeichenfolge wie "rm -rf etwas Wichtiges" generiert wird, besteht kein Risiko für die Code-Injektion (aber bitte denken Sie daran, es ist sehr, sehr im allgemeinen Fall schwer zu gewährleisten). Wenn Sie im Browser arbeiten, ist die Code-Injection meines Erachtens ein recht geringes Risiko.

Die Leistung muss gegen die einfache Codierung abgewogen werden. Ich bin der Meinung, dass Sie beim Parsen der Formel das Ergebnis genauso gut während des Parsens berechnen können, anstatt einen anderen Parser auszuführen (den in eval ()). Es kann jedoch einfacher sein, mit eval () zu codieren, und der Leistungseinbruch wird wahrscheinlich nicht bemerkt. Es sieht so aus, als ob eval () in diesem Fall nicht böser ist als jede andere Funktion, die Ihnen möglicherweise Zeit sparen könnte.

user27476
quelle
78
Sie sprechen nicht das Problem des Codes an, bei dem eval schwer zu debuggen ist
Bobobobo
48
Code Injection ist ein sehr ernstes Problem für Javascript, wenn Sie sich überhaupt Gedanken über die Daten Ihres Benutzers machen. Injizierter Code wird (im Browser) so ausgeführt, als ob er von Ihrer Site stammt, und lässt ihn jede Art von Spielerei ausführen, die der Benutzer manuell ausführen könnte. Wenn Sie zulassen, dass (Drittanbieter-) Code auf Ihre Seite gelangt, kann er im Auftrag Ihres Kunden Dinge bestellen oder dessen Gravatar ändern oder was auch immer er über Ihre Website tun könnte. Sei sehr vorsichtig. Es ist genauso schlimm, wenn Hacker Ihre Kunden besitzen, wie wenn sie Ihren Server besitzen.
Sean McMillan
71
Wenn die Daten von Ihrem Server kommen und von Ihnen als Entwickler generiert wurden, kann die Verwendung von eval () nicht schaden. Der wahre Schaden besteht darin, alles zu glauben, was Sie lesen. Sie sehen viele Leute, die sagen, eval () sei böse und sie haben keine Ahnung warum, außer dass sie es irgendwo lesen.
Vince Panuccio
42
@ Sean McMillan: Ich möchte Ihnen glauben, aber wenn jemand Javascript eval()von Ihrem Server abfängt und ändert , kann er auch einfach die Quelle der Seite ändern und die Kontrolle über die Benutzerinformationen übernehmen. . . Ich sehe den Unterschied nicht.
Walt W
20
Zu "Code-Injection - ... Auch hier hat JavaScript in einem Browser dieses Problem nicht." & "Wenn Sie im Browser arbeiten, ist die Code-Injection meines Erachtens ein ziemlich geringes Risiko." Schlagen Sie vor, dass die Code-Injection im Browser kein Problem darstellt? XSS gehört seit mehreren Jahren zu den Top 3 der OWASP-Top-10-Liste.
Mike Samuel
72

eval()ist nicht böse Oder wenn ja, ist es genauso böse wie Reflection, Datei- / Netzwerk-E / A, Threading und IPC in anderen Sprachen "böse".

Wenn für Ihren Zweck , eval()schneller als die manuelle Interpretation ist, oder macht den Code einfacher oder mehr klar ... dann sollten Sie es verwenden. Wenn beides nicht, dann solltest du es nicht. So einfach ist das.

Shog9
quelle
5
Ein solcher Zweck könnte darin bestehen, optimierten Code zu generieren, der entweder zu lang oder zu repetitiv wäre, um von Hand geschrieben zu werden. Die Art von Sachen, die in LISP ein Makro erfordern würden.
Wberry
5
Dies ist ein so allgemeiner Rat, dass er buchstäblich auf jeden vorhandenen Codeblock angewendet werden kann. Es fügt dieser Frage wirklich nichts hinzu; Insbesondere hilft es niemandem, hierher zu kommen, festzustellen, ob seine spezielle Verwendung problematisch ist oder nicht.
jpmc26
2
Schneller, einfacher, klarer ... Diese Antwort deckt die Auswirkungen auf die Sicherheit nicht gut genug ab.
Ruud Helderman
55

Wenn Sie der Quelle vertrauen.

Im Fall von JSON ist es mehr oder weniger schwierig, die Quelle zu manipulieren, da sie von einem von Ihnen kontrollierten Webserver stammt. Solange der JSON selbst keine Daten enthält, die ein Benutzer hochgeladen hat, gibt es keinen wesentlichen Nachteil bei der Verwendung von eval.

In allen anderen Fällen würde ich große Anstrengungen unternehmen, um sicherzustellen, dass vom Benutzer bereitgestellte Daten meinen Regeln entsprechen, bevor ich sie an eval () weitergebe.

Tomalak
quelle
13
Ein JSON-String sollte immer anhand der JSON-Grammatik getestet werden, bevor er in eval () verwendet wird. Die JSON-Zeichenfolge "{foo: alert ('XSS')}" würde also nicht übergeben, da "alert ('XSS')" kein geeigneter Wert ist.
Gumbo
3
Verwenden Sie dann HTTPS. OTOH: Man-in-the-Middle ist kein typisches Angriffsszenario für die Web-App Garden Varieté, dh Cross-Site-Scripting.
Tomalak
7
evalanalysiert auch nicht alle gültigen JSON-Zeichenfolgen korrekt. Zum Beispiel wird JSON.parse(' "\u2028" ') === "\u2028"jedoch eval(' "\u2028" ')eine Ausnahme ausgelöst, da U + 2028 eine neue Zeile in JavaScript ist, für JSON jedoch keine neue Zeile.
Mike Samuel
1
@Justin - Wenn das Protokoll kompromittiert ist, wurde normalerweise das anfängliche Laden der Seite über dasselbe Protokoll gesendet, und dann ist dies ein strittiger Punkt, da der Client bereits so kompromittiert ist, wie es nur sein kann.
Antinom
1
Wunderschön gesagt @Tomalak, ich habe dies gerade in meiner Antwort erwähnt! Genial!
NiCk Newman
25

Lassen Sie uns echte Leute bekommen:

  1. Jeder große Browser verfügt jetzt über eine integrierte Konsole, mit der Ihr potenzieller Hacker jede Funktion mit beliebigem Wert aufrufen kann. Warum sollten sie sich die Mühe machen, eine Eval-Anweisung zu verwenden, selbst wenn dies möglich ist?

  2. Wenn das Kompilieren von 2000 Zeilen JavaScript 0,2 Sekunden dauert, wie hoch ist mein Leistungsabfall, wenn ich vier Zeilen JSON auswerte?

Sogar Crockfords Erklärung für "Eval is Evil" ist schwach.

eval is Evil. Die eval-Funktion ist die am häufigsten missbrauchte Funktion von JavaScript. Vermeide es

Wie Crockford selbst sagen könnte: "Diese Art von Aussage führt tendenziell zu irrationaler Neurose. Kaufen Sie sie nicht."

Eval zu verstehen und zu wissen, wann es nützlich sein könnte, ist viel wichtiger. Beispielsweise ist eval ein sinnvolles Tool zur Auswertung von Serverantworten, die von Ihrer Software generiert wurden.

Übrigens: Prototype.js ruft eval fünfmal direkt auf (einschließlich evalJSON () und evalResponse ()). jQuery verwendet es in parseJSON (über den Funktionskonstruktor).

Arbeitstier
quelle
10
JQuery verwendet die im Browser integrierte Funktion JSON.parse, sofern verfügbar (die viel schneller und sicherer ist) und verwendet eval nur als Fallback-Mechanismus. Die Aussage "eval is evil" ist eine einigermaßen gute Richtlinie.
jjmontes
30
Betreff "Jeder große Browser hat jetzt eine eingebaute Konsole ...". Die Code-Injektion ist ein Problem, wenn ein Benutzer Code eingeben kann, der dann im Browser eines anderen Benutzers ausgeführt wird. Browserkonsolen erlauben es einem Benutzer nicht, Code in einem Browser eines anderen Benutzers auszuführen, sodass sie für die Entscheidung, ob es sich lohnt, sich vor Code-Injection zu schützen, irrelevant sind.
Mike Samuel
28
"Jeder große Browser hat jetzt eine eingebaute Konsole ... warum sollten sie sich die Mühe machen, eine Bewertung zu verwenden?" - Sie sind weit weg von der Marke. Ich schlage vor, Sie bearbeiten die Antwort. Die Fähigkeit eines Benutzers, Code einzufügen, der im Browser eines anderen Benutzers ausgeführt werden kann, ist ein Hauptproblem. Und hier müssen Sie wirklich real werden.
Akkishore
5
@akkishore, ich würde mich freuen, wenn Sie ein Beispiel aus dem wirklichen Leben finden, das Ihre überbewerteten Aussagen unterstützt.
Akash Kava
7
@AkashKava Was Sie nicht erkennen, ist, dass wenn ich Javascript in meinem Kommentarfeld einreiche und dieses Javascript es in die Datenbank schafft. Wenn ein anderer Benutzer diesen Kommentar anzeigt (in den ich Javascript eingefügt habe), nimmt eval dieses Javascript beim Rendern und wertet es mit dem Interpreter aus, wodurch mein eingebettetes Javascript im Browser des anderen Benutzers ausgeführt wird. Auf diese Weise kann ich alle Arten von Informationen erfassen. Ihr Benutzername, ihre Benutzer-ID in der Datenbank, ihre E-Mail-Adresse usw. Dies ist keine schwierige Antwort. Wenn Sie XSS gegoogelt hätten, würden Sie in etwa 10 Sekunden sehen, warum dies ein Problem ist.
Kyle Richter
18

Ich neige dazu , folgen Crockford Beratung für eval()und vermeiden sie ganz. Selbst Wege, die dies zu erfordern scheinen, tun dies nicht. Mit können Sie beispielsweise setTimeout()eine Funktion übergeben, anstatt sie auszuwerten.

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

Selbst wenn es sich um eine vertrauenswürdige Quelle handelt, verwende ich sie nicht, da der von JSON zurückgegebene Code möglicherweise verstümmelt ist, was bestenfalls etwas Wackeliges bewirken kann, im schlimmsten Fall etwas Schlechtes aufdeckt.

Swilliams
quelle
2
Ich denke, dass Fehler im JSON-Formatierer auf der Serverseite sicherlich ein Problem sind. Hängt die Antwort vom Server von der Art des vom Benutzer übermittelten Textes ab? Dann musst du auf XSS achten.
Swilliams
3
Wenn Ihr Webserver nicht über HTTPS authentifiziert ist, kann es zu einem Man-in-the-Middle-Angriff kommen, bei dem ein anderer Host die Anforderung abfängt und seine eigenen Daten sendet.
Ben Combee
11
Wenn jemand einen Man-in-the-Middle-Angriff ausführen kann, kann er Ihren Skripten problemlos alles hinzufügen.
el.pescado
10
Sie sollten sich überhaupt nicht auf Ihren Javascript-Code verlassen ... Sie verlassen sich nicht auf irgendetwas, das auf der Clientseite ausgeführt wird ... Wenn jemand einen Man-in-the-Middle-Angriff ausführt, warum sollte er sich dann mit Ihren JSON-Objekten anlegen? Er kann Ihnen eine andere Webseite und verschiedene js-Dateien zur Verfügung stellen ...
Calmarius
5
Ich persönlich mag das Argument "Es gibt immer andere Möglichkeiten, es zu tun" nicht. Zum Beispiel könnte man auch sagen, dass es immer Möglichkeiten gibt, objektorientierte Programmierung zu vermeiden. Das heißt nicht, dass es keine gute Option ist. Wenn Sie eval und seine Gefahren verstehen, kann es ein großartiges Werkzeug sein, um es in den richtigen Situationen einzusetzen.
Dallas
4

Ich habe gesehen, wie Leute befürwortet haben, eval nicht zu verwenden, weil es böse ist , aber ich habe gesehen, dass dieselben Leute Function und setTimeout dynamisch verwenden, also haben sie eval unter der Haube verwendet : D.

Übrigens, wenn Ihre Sandbox nicht sicher genug ist (z. B. wenn Sie an einer Site arbeiten, die Code-Injection ermöglicht), ist die Auswertung das letzte Ihrer Probleme. Die Grundregel der Sicherheit lautet, dass alle Eingaben böse sind, aber im Fall von JavaScript kann sogar JavaScript selbst böse sein, da Sie in JavaScript jede Funktion überschreiben können und einfach nicht sicher sein können, ob Sie die echte verwenden. Wenn ein bösartiger Code vor Ihnen gestartet wird, können Sie keiner integrierten JavaScript-Funktion vertrauen: D.

Der Nachwort zu diesem Beitrag lautet nun:

Wenn Sie es WIRKLICH brauchen (80% der Zeit wird keine Bewertung benötigt) und Sie sicher sind, was Sie tun, verwenden Sie einfach Bewertung (oder bessere Funktion;)), Verschlüsse und OOP decken die 80/90% der In dem Fall, in dem eval durch eine andere Art von Logik ersetzt werden kann, besteht der Rest aus dynamisch generiertem Code (z. B. wenn Sie einen Interpreter schreiben) und wie Sie bereits gesagt haben, JSON evaluiert (hier können Sie die sichere Crockford-Evaluierung verwenden;))

Kentaromiura
quelle
Und wie Crockford selbst betont hat, verfügen aktuelle Webbrowser über eine integrierte Funktion JSON.parse .
Ruud Helderman
4

Eval ist eine Ergänzung zur Kompilierung, die bei der Vorlage des Codes verwendet wird. Mit Vorlagen meine ich, dass Sie einen vereinfachten Vorlagengenerator schreiben, der nützlichen Vorlagencode generiert, der die Entwicklungsgeschwindigkeit erhöht.

Ich habe ein Framework geschrieben, in dem Entwickler EVAL nicht verwenden, aber sie verwenden unser Framework, und dieses Framework muss wiederum EVAL verwenden, um Vorlagen zu generieren.

Die Leistung von EVAL kann mithilfe der folgenden Methode gesteigert werden. Anstatt das Skript auszuführen, müssen Sie eine Funktion zurückgeben.

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

Es sollte organisiert sein als

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

var a = f(3,5);

Das Zwischenspeichern von f wird sicherlich die Geschwindigkeit verbessern.

Auch Chrome ermöglicht das Debuggen solcher Funktionen sehr einfach.

In Bezug auf die Sicherheit wird die Verwendung von eval oder nicht kaum einen Unterschied machen.

  1. Zunächst ruft der Browser das gesamte Skript in einer Sandbox auf.
  2. Jeder Code, der in EVAL böse ist, ist im Browser selbst böse. Der Angreifer oder jeder andere kann problemlos einen Skriptknoten in DOM einfügen und alles tun, wenn er etwas bewerten kann. Wenn Sie EVAL nicht verwenden, macht dies keinen Unterschied.
  3. Es ist meistens eine schlechte serverseitige Sicherheit, die schädlich ist. Eine schlechte Cookie-Validierung oder eine schlechte ACL-Implementierung auf dem Server verursacht die meisten Angriffe.
  4. In Javas nativem Code war kürzlich eine Java-Sicherheitslücke usw. vorhanden. JavaScript wurde und wird für die Ausführung in einer Sandbox entwickelt, während Applets für die Ausführung außerhalb einer Sandbox mit Zertifikaten usw. entwickelt wurden, die zu Sicherheitslücken und vielen anderen Dingen führen.
  5. Das Schreiben von Code zum Imitieren eines Browsers ist nicht schwierig. Sie müssen lediglich eine HTTP-Anfrage an den Server mit Ihrer bevorzugten Benutzeragentenzeichenfolge senden. Alle Testtools verspotten ohnehin Browser. Wenn ein Angreifer Ihnen Schaden zufügen möchte, ist EVAL das letzte Mittel. Sie haben viele andere Möglichkeiten, um mit Ihrer serverseitigen Sicherheit umzugehen.
  6. Das Browser-DOM hat keinen Zugriff auf Dateien und keinen Benutzernamen. Tatsächlich kann nichts auf der Maschine, auf das eval zugreifen kann.

Wenn Ihre serverseitige Sicherheit solide genug ist, damit jeder von überall angreifen kann, sollten Sie sich keine Sorgen um EVAL machen. Wie bereits erwähnt, haben Angreifer unabhängig von der EVAL-Fähigkeit Ihres Browsers viele Tools, mit denen sie sich in Ihren Server hacken können, wenn EVAL nicht vorhanden wäre.

Eval eignet sich nur zum Generieren einiger Vorlagen für die Verarbeitung komplexer Zeichenfolgen, die auf etwas basieren, das nicht im Voraus verwendet wird. Zum Beispiel werde ich bevorzugen

"FirstName + ' ' + LastName"

Im Gegensatz zu

"LastName + ' ' + FirstName"

Als mein Anzeigename, der aus einer Datenbank stammen kann und der nicht fest codiert ist.

Akash Kava
quelle
Sie können die Funktion anstelle von eval - verwenden function (first, last) { return last + ' ' + first }.
Konrad Borowski
Die Namen der Spalten stammen aus der Datenbank.
Akash Kava
3
Die Bedrohung evalbesteht hauptsächlich aus anderen Benutzern . Angenommen, Sie haben eine Einstellungsseite, auf der Sie festlegen können, wie Ihr Name anderen angezeigt wird. Nehmen wir auch an, Sie haben beim Schreiben nicht sehr klar gedacht, sodass Ihr Auswahlfeld Optionen wie enthält <option value="LastName + ' ' + FirstName">Last First</option>. Ich öffne meine Entwicklungstools, ändere die valueOption in alert('PWNED!'), wähle die geänderte Option aus und sende das Formular. Jedes Mal, wenn eine andere Person meinen Anzeigenamen sehen kann, wird dieser Code ausgeführt.
CHao
@cHao, Das, von dem Sie sprechen, ist ein Beispiel für schlechte serverseitige Sicherheit. Der Server sollte niemals Daten akzeptieren, die als Code in einem beliebigen Browser ausgeführt werden können. Wieder einmal haben Sie das Konzept einer schlechten serverseitigen Sicherheit nicht verstanden.
Akash Kava
1
Sie können über die serverseitige Sicherheit jammern, wenn Sie möchten, aber der ganze Sinn evalbesteht darin, Code auszuführen, der nicht Teil des von Ihnen geschriebenen Skripts ist. Wenn Sie dazu nicht die Kraft benötigen (und dies fast nie tun), evalhilft das Vermeiden , eine ganze Kategorie von Problemen zu vermeiden. Das ist eine gute Sache, wenn Ihr serverseitiger Code nicht perfekt ist.
CHao
4

Beim Debuggen in Chrome (v28.0.1500.72) stellte ich fest, dass Variablen nicht an Abschlüsse gebunden sind, wenn sie nicht in einer verschachtelten Funktion verwendet werden, die den Abschluss erzeugt. Ich denke, das ist eine Optimierung der JavaScript-Engine.

ABER : Wenn eval()es innerhalb einer Funktion verwendet wird, die einen Abschluss verursacht, sind ALLE Variablen der äußeren Funktionen an den Abschluss gebunden, auch wenn sie überhaupt nicht verwendet werden. Wenn jemand die Zeit hat zu testen, ob dadurch Speicherlecks entstehen können, hinterlassen Sie mir bitte unten einen Kommentar.

Hier ist mein Testcode:

(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();
})();

Ich möchte hier darauf hinweisen, dass eval () nicht unbedingt auf die native eval()Funktion verweisen muss . Es hängt alles vom Namen der Funktion ab . Wenn Sie also den Native eval()mit einem Aliasnamen aufrufen (z. B. var noval = eval;und dann in einer inneren Funktion noval(expression);), schlägt die Auswertung von expressionmöglicherweise fehl, wenn auf Variablen verwiesen wird, die Teil des Abschlusses sein sollten, dies jedoch nicht ist.

Benjamin
quelle
3

Endeffekt

Wenn Sie den Code erstellt oder bereinigt haben eval, ist er niemals böse .

Etwas detaillierter

evalist böse, wenn auf dem Server Eingaben ausgeführt werden, die von einem Client übermittelt wurden, der nicht vom Entwickler erstellt oder vom Entwickler nicht bereinigt wurde .

evalist nicht böse, wenn es auf dem Client ausgeführt wird, selbst wenn vom Client erstellte nicht bereinigte Eingaben verwendet werden .

Natürlich sollten Sie die Eingabe immer bereinigen, um die Kontrolle darüber zu haben, was Ihr Code verbraucht.

Argumentation

Der Client kann jeden gewünschten Code ausführen, auch wenn der Entwickler ihn nicht codiert hat. Dies gilt nicht nur für das, was bewertet wird, sondern auch für den Ruf an sich evalselbst .

Steven Spungin
quelle
2

Die einzige Instanz, in der Sie eval () verwenden sollten, ist, wenn Sie dynamisches JS im laufenden Betrieb ausführen müssen. Ich spreche von JS, das Sie asynchron vom Server herunterladen ...

... und 9 mal von 10 könnten Sie dies leicht vermeiden, indem Sie umgestalten.

Oli
quelle
Heutzutage gibt es andere (und bessere) Möglichkeiten, JavaScript asynchron vom Server zu laden
Ruud Helderman
1

evalist selten die richtige Wahl. Zwar gibt es zahlreiche Fälle geben kann , wo Sie erreichen können , was Sie ein Skript erreichen müssen gemeinsam durch Verketten und es on the fly läuft, müssen Sie in der Regel viel leistungsfähiger und wartbar Techniken zur Verfügung: assoziatives Array - Notation ( obj["prop"]ist die gleiche wie obj.prop) , Verschlüsse, objektorientierte Techniken, Funktionstechniken - verwenden Sie sie stattdessen.

Yfeldblum
quelle
1

Was das Client-Skript betrifft, denke ich, dass das Thema Sicherheit ein strittiger Punkt ist. Alles, was in den Browser geladen wird, kann manipuliert werden und sollte als solches behandelt werden. Die Verwendung einer eval () - Anweisung birgt kein Risiko, wenn es viel einfachere Möglichkeiten gibt, JavaScript-Code auszuführen und / oder Objekte im DOM zu bearbeiten, z. B. die URL-Leiste in Ihrem Browser.

javascript:alert("hello");

Wenn jemand sein DOM manipulieren will, sage ich wegschwingen. Die Sicherheit zur Verhinderung jeglicher Art von Angriff sollte immer in der Verantwortung der Serveranwendung liegen.

Aus pragmatischer Sicht hat die Verwendung von eval () in einer Situation, in der Dinge anders gemacht werden können, keinen Vorteil. Es gibt jedoch spezielle Fälle, in denen eine Bewertung verwendet werden sollte. Wenn ja, kann dies definitiv ohne das Risiko einer Explosion der Seite durchgeführt werden.

<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>
Peter Mortensen
quelle
6
Betreff "Die Verwendung einer eval () - Anweisung birgt kein Risiko, wenn es viel einfachere Möglichkeiten gibt, Javascript auszuführen und / oder Objekte im DOM zu bearbeiten." Die Code-Injektion ist ein Problem, wenn ein Benutzer Code eingeben kann, der dann im Browser eines anderen Benutzers ausgeführt wird. Browserkonsolen erlauben es einem Benutzer nicht, Code in einem Browser eines anderen Benutzers auszuführen, sodass sie für die Entscheidung, ob es sich lohnt, sich vor Code-Injection zu schützen, irrelevant sind.
Mike Samuel
Wird nicht <head></head>benötigt, auch wenn leer?
Peter Mortensen
2
Diese Antwort ignoriert die Risiken von XSS vollständig .
Ruud Helderman
1

Auf der Serverseite ist eval nützlich, wenn Sie mit externen Skripten wie SQL oder Influxdb oder Mongo arbeiten. Wo eine benutzerdefinierte Validierung zur Laufzeit durchgeführt werden kann, ohne dass Ihre Dienste erneut bereitgestellt werden müssen.

Zum Beispiel ein Leistungsservice mit folgenden Metadaten

{
  "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\"}`"
  }
}

Was dann erlaubt,

  • Direkte Einfügung von Objekten / Werten durch eine Literalzeichenfolge in einen JSON, nützlich zum Vorlagen von Texten

  • Kann als Vergleich verwendet werden, sagen wir, wir machen Regeln, wie Quest oder Ereignisse in CMS validiert werden

Con davon:

  • Kann Fehler im Code sein und Dinge im Dienst auflösen, wenn sie nicht vollständig getestet wurden.

  • Wenn ein Hacker ein Skript auf Ihrem System schreiben kann, sind Sie ziemlich durcheinander.

  • Eine Möglichkeit, Ihr Skript zu validieren, besteht darin, den Hash Ihrer Skripte an einem sicheren Ort aufzubewahren, damit Sie sie vor dem Ausführen überprüfen können.

MichaelC
quelle
Nett. Als ich die Frage stellte, hatte ich nicht einmal an serverseitiges JS gedacht.
Richard Turner
1

Ich denke, Fälle, in denen eine Bewertung gerechtfertigt ist, wären selten. Es ist wahrscheinlicher, dass Sie es verwenden, wenn Sie denken, dass es gerechtfertigt ist, als wenn Sie es verwenden, wenn es tatsächlich gerechtfertigt ist.

Die Sicherheitsprobleme sind die bekanntesten. Beachten Sie jedoch auch, dass JavaScript die JIT-Kompilierung verwendet und dies mit eval sehr schlecht funktioniert. Eval ist für den Compiler eine Art Blackbox, und JavaScript muss in der Lage sein, Code (bis zu einem gewissen Grad) im Voraus vorherzusagen, um Leistungsoptimierungen und Scoping sicher und korrekt anwenden zu können. In einigen Fällen kann sich die Auswirkung auf die Leistung sogar auf anderen Code außerhalb von eval auswirken.

Wenn Sie mehr wissen möchten: https://github.com/getify/You-Dont-Know-JS/blob/master/scope%20%26%20closures/ch2.md#eval

Andre O.
quelle
0

Es ist in Ordnung, es zu verwenden, wenn Sie die vollständige Kontrolle über den Code haben, der an die evalFunktion übergeben wird.

John Topley
quelle
2
Wenn Sie die vollständige Kontrolle darüber haben, an was Sie übergeben, evalstellt sich die große Frage: Wann ist es sinnvoll, dass dies eher eine Zeichenfolge als eine echte JS ist?
CHao
@cHao Wenn Sie beispielsweise eine große Spieleanwendung (5-10 MB Javascript) haben, ist es besser, zuerst einen einfachen schnell ladenden AJAX-Preloader (1 KB) zu erstellen, der das große Hauptskript lädt, während ein Lade- angezeigt wird. Bar oder ähnliches. Nach dem Herunterladen können Sie "eval (Quelle)" oder besser "neue Funktion (Quelle)" verwenden, um das geladene Game-Application-Script auszuführen. Auf diese Weise kann der Benutzer visuell sehen, dass die Anwendung Zeit zum Herunterladen benötigt, bis das Spiel gestartet werden kann. Ohne dies muss der Benutzer warten, bis die gesamte Anwendung ohne visuelles Feedback geladen ist.
SammieFox
@SammieFox Es gibt andere (und bessere) Möglichkeiten, dies zu tun, insbesondere <script async="true" src="...">. Siehe auch: w3bits.com/async-javascript
Ruud Helderman
Die Antwort ist ein gefährlicher Rat; Zu viele Entwickler haben das falsche Gefühl, die Kontrolle zu haben. Der Rat ist sinnvoll für Software, die nicht mehr aktiv gewartet wird. Eine solche Software sollte jedoch als tot angesehen werden.
Ruud Helderman
0

Wenn möglich nur während des Testens. Beachten Sie auch, dass eval () viel langsamer ist als andere spezialisierte JSON usw.-Evaluatoren.

Eric Wendelin
quelle
0

Es gibt keinen Grund, eval () nicht zu verwenden, solange Sie sicher sein können, dass die Quelle des Codes von Ihnen oder dem tatsächlichen Benutzer stammt. Obwohl er manipulieren kann, was in die eval () - Funktion gesendet wird, ist dies kein Sicherheitsproblem, da er den Quellcode der Website manipulieren und daher den JavaScript-Code selbst ändern kann.

Also ... wann sollte man eval () nicht verwenden? Eval () sollte nur dann nicht verwendet werden, wenn die Möglichkeit besteht, dass Dritte dies ändern. Wie das Abfangen der Verbindung zwischen dem Client und Ihrem Server (aber wenn das ein Problem ist, verwenden Sie HTTPS). Sie sollten eval () nicht für das Parsen von Code verwenden, der von anderen wie in einem Forum geschrieben wurde.

Georg Schölly
quelle
Betreff "Es gibt keinen Grund, eval () nicht zu verwenden, solange Sie sicher sein können, dass die Quelle des Codes von Ihnen oder dem tatsächlichen Benutzer stammt." Dies setzt voraus, dass es einen einzelnen Benutzer gibt. Diese Prämisse ist im OP nicht angegeben. Wenn es mehrere Benutzer gibt, kann die Unachtsamkeit evaleiner Zeichenfolge, die aus Inhalten eines Benutzers besteht, es diesem Benutzer ermöglichen, Code im Browser des anderen Benutzers auszuführen.
Mike Samuel
@ MikeSamuel, eval kann Code im Browser eines anderen Benutzers ausführen. Ich habe das noch nicht gehört. Hast du das versucht? Dies ist in der Geschichte des Surfens noch nie passiert. Können Sie uns ein Beispiel zeigen?
Akash Kava
@AkashKava, Eine Zeichenfolge kann von einem Benutzeragenten stammen, in einer Datenbank gespeichert und dann einem anderen Browser bereitgestellt werden eval. Es passiert ständig.
Mike Samuel
@ MikeSamuel Datenbank? wo? Wer serviert falsche Saite? Ist nicht die Datenbank auf der Serverseite schuld? Zunächst einmal ist EVAL nicht für schlecht geschriebenen serverseitigen Code verantwortlich zu machen. Verwenden Sie jsfiddle und zeigen Sie der Welt ein Beispiel aus der Praxis, wo es Schaden anrichten kann.
Akash Kava
2
@AkashKava, ich verstehe deine Frage nicht. Wir sprechen nicht über eine bestimmte Anwendung, sondern über Gründe, sie nicht zu verwenden eval. Wie ist es nützlich, dem Server die Schuld zu geben? Wenn jemand beschuldigt werden sollte, sollte es der Angreifer sein. Unabhängig von der Schuld ist ein Client, der trotz Fehlern auf dem Server nicht für XSS anfällig ist, besser als ein Client, der anfällig ist, wenn alle anderen gleich sind.
Mike Samuel
0

Wenn es wirklich nötig ist, ist eval nicht böse. Aber 99,9% der Verwendungen von eval, über die ich stolpere, werden nicht benötigt (ohne setTimeout-Zeug).

Für mich ist das Böse keine Leistung oder gar ein Sicherheitsproblem (indirekt ist es beides). All diese unnötigen Verwendungen von eval tragen zu einer Wartungshölle bei. Refactoring-Tools werden abgeworfen. Die Suche nach Code ist schwierig. Unerwartete Auswirkungen dieser Bewertungen sind zahlreich.

PEZ
quelle
5
eval ist für setTimeout nicht erforderlich. Dort können Sie auch eine Funktionsreferenz verwenden.
Matthew Crumley
0

Wann ist eval () von JavaScript nicht böse?

Ich versuche immer davon abzuraten, eval zu verwenden . Fast immer ist eine sauberere und wartbarere Lösung verfügbar. Eval wird nicht einmal für das JSON-Parsing benötigt . Eval trägt zur Hölle der Wartung bei . Nicht ohne Grund wird es von Meistern wie Douglas Crockford verpönt.

Aber ich habe ein Beispiel gefunden, wo es verwendet werden sollte :

Wenn Sie den Ausdruck übergeben müssen.

Zum Beispiel habe ich eine Funktion, die ein allgemeines google.maps.ImageMapTypeObjekt für mich erstellt, aber ich muss ihm das Rezept mitteilen, wie die Kachel-URL aus den Parametern zoomund erstellt coordwerden soll:

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);
        },
        ....
    });
}
TMS
quelle
3
Es sieht so aus, als könnte es überarbeitet werden, sodass eval () nicht erforderlich ist - tileURLexpr ist nur eine Vorlage, sodass eine vernünftige Verwendung von replace () die Aufgabe übernehmen würde. Es erinnert mich jedoch an ein Beispiel, an das ich gedacht habe, als ich die Frage eingereicht habe, bei dem es einem Benutzer ermöglicht wurde, eine auszuwertende mathematische Formel anzugeben, ähnlich der Tabellenkalkulationsfunktion. Natürlich habe ich das damals nicht erwähnt, weil ich die Antworten nicht beeinflussen wollte!
Richard Turner
8
tileURL: function (zoom, coord) { return 'http://tile.openstreetmap.org/' + b + '/' + a.x + '/' + a.y + '.png'; },
Casey Chu
0

Mein Beispiel für die Verwendung von eval: import .

Wie es normalerweise gemacht wird.

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

Aber mit Hilfe evalund einer kleinen Hilfsfunktion sieht es viel besser aus:

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

importable könnte so aussehen (diese Version unterstützt nicht das Importieren konkreter Elemente).

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;
}
Jaroslaw
quelle
2
+1 für die Idee, aber Sie haben hier einen Fehler : .replace(/name/g, name).replace('path', path). Wenn nameder String enthalten "path"ist, kann es zu Überraschungen kommen.
Wberry
1
Das Deklarieren einer Variablen für jede Eigenschaft von componentsist ein möglicher Codegeruch. Durch die Umgestaltung Ihres Codes wird das Problem möglicherweise vollständig behoben. Ihre aktuelle Lösung ist nur syntaktischer Zucker. Wenn Sie darauf bestehen, würde ich empfehlen, einen eigenen Präprozessor zu schreiben, der vor der Bereitstellung ausgeführt wird. Das sollte evalsich vom Produktionscode fernhalten .
Ruud Helderman
0

Eval ist nicht böse, nur missbraucht.

Wenn Sie den darin enthaltenen Code erstellt haben oder ihm vertrauen können, ist dies in Ordnung. Die Leute reden immer wieder darüber, dass Benutzereingaben bei eval keine Rolle spielen. Na irgendwie ~

Wenn Benutzereingaben an den Server gesendet werden, wird diese an den Client zurückgegeben, und dieser Code wird in eval verwendet, ohne bereinigt zu werden. Herzlichen Glückwunsch, Sie haben die Pandora-Box geöffnet, damit Benutzerdaten an jeden gesendet werden können.

Je nachdem, wo sich die Bewertung befindet, verwenden viele Websites SPAs, und die Bewertung könnte dem Benutzer den Zugriff auf Anwendungsinternale erleichtern, die sonst nicht einfach gewesen wären. Jetzt können sie eine gefälschte Browser-Erweiterung erstellen, die diese Auswertung aufzeichnet und erneut Daten stiehlt.

Ich muss nur herausfinden, wozu Sie die Bewertung verwenden. Das Generieren von Code ist nicht wirklich ideal, wenn Sie einfach Methoden erstellen können, um solche Dinge zu tun, Objekte zu verwenden oder dergleichen.

Nun ein schönes Beispiel für die Verwendung von eval. Ihr Server liest die von Ihnen erstellte Swagger-Datei. Viele der URL-Parameter werden im Format erstellt {myParam}. Sie möchten also die URLs lesen und sie dann in Vorlagenzeichenfolgen konvertieren, ohne komplexe Ersetzungen vornehmen zu müssen, da Sie über viele Endpunkte verfügen. Sie können also so etwas tun. Beachten Sie, dass dies ein sehr einfaches Beispiel ist.

const params = { id: 5 };

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

// use eval(route); to do something
jemiloii
quelle
-1

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 Hyperskript konvertiert . 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 auch in einer solchen Situation kein Problem, da Sie die generierte Zeichenfolge nur einmal interpretieren und dann die ausführbare Ausgabe mehrmals wiederverwenden müssen.

Sie können sehen, wie die Codegenerierung erreicht wurde, wenn Sie neugierig sind .

Gesehen
quelle
"Das Hyperskript wird zuerst als Zeichenfolge generiert (...)" Es ist sinnvoller, die gesamte Codegenerierung in der Erstellungsphase durchzuführen, den resultierenden Hyperskriptcode in eine separate ausführbare Datei (.js) zu schreiben und diese Datei dann zum Testen und Bereitstellen bereitzustellen Produktion. Ich mag die Art und Weise, wie Sie die Codegenerierung verwenden. Es ist nur evalein Hinweis darauf, dass eine Verantwortung, die zur Kompilierungszeit gehört, in die Laufzeit übergegangen ist.
Ruud Helderman
-1

Meiner Meinung nach ist eval eine sehr leistungsfähige Funktion für clientseitige Webanwendungen und sicher ... So sicher wie JavaScript, die es nicht sind. :-) Die Sicherheitsprobleme sind im Wesentlichen ein serverseitiges Problem, da Sie jetzt mit einem Tool wie Firebug jede JavaScript-Anwendung angreifen können.

Peter Mortensen
quelle
1
Die Verwendung von evalmuss gegen XSS-Angriffe gesichert werden, was nicht immer einfach ist.
Benjamin
-1

Eval ist nützlich für die Codegenerierung, wenn Sie keine Makros haben.

Zum Beispiel (ein dummes) Beispiel, wenn Sie einen Brainfuck- Compiler schreiben , möchten Sie wahrscheinlich eine Funktion erstellen, die die Befehlsfolge als Zeichenfolge ausführt, und sie auswerten, um eine Funktion zurückzugeben.

Erik Haliewicz
quelle
Entweder schreiben Sie einen Compiler (der den generierten Code speichert, anstatt ihn auszuführen) oder Sie schreiben einen Interpreter (wobei jeder Befehl eine vorkompilierte Implementierung hat). Weder ist ein Anwendungsfall für eval.
Ruud Helderman
Wenn Sie Javascript-Code generiert haben und diesen sofort ausführen möchten (z. B. für Leistungsvorteile gegenüber direkter Interpretation), wäre dies ein Anwendungsfall für die Bewertung.
Erik Haliewicz
Guter Punkt; Ich habe in diesem Artikel ein Beispiel über Blockly gesehen . Ich bin schockiert, dass Google empfiehlt eval, wenn die Alternative ( Funktion ) sowohl schneller ( wie in MDN erläutert ) als auch zuverlässiger ist (verhindert unvorhersehbare Fehler durch eine bessere Isolierung zwischen dem generierten Code und anderem "unterstützenden" Code auf derselben Webseite).
Ruud Helderman
-5

Wenn Sie eine JSON-Struktur mit einer Analysefunktion analysieren (z. B. jQuery.parseJSON), wird eine perfekte Struktur der JSON-Datei erwartet (jeder Eigenschaftsname steht in doppelten Anführungszeichen). JavaScript ist jedoch flexibler. Daher können Sie eval () verwenden, um dies zu vermeiden.

Vitmalina
quelle
4
Nicht blind benutzen eval, insb. beim Abrufen von JSON-Daten von einer Drittanbieterquelle. Siehe JSON.Stringify ohne Anführungszeichen für Eigenschaften? für den korrekten Ansatz zum Parsen von "JSON ohne Anführungszeichen".
Rob W
2
Wenn für Eigenschaftsnamen keine doppelten Anführungszeichen verwendet werden, handelt es sich möglicherweise um eine Zeichenfolgendarstellung eines Objektliteral, es handelt sich jedoch nicht um JSON . JSON definiert die Eigenschaftsnamen als a stringund a stringals eine Folge von null oder mehr Unicode-Zeichen, die in doppelte Anführungszeichen gesetzt werden und Backslash-Escapezeichen verwenden.
Nutzloser Code
Siehe Artikel von Nikolas Zakas - "eval () ist nicht böse, nur missverstanden" nczonline.net/blog/2013/06/25/eval-isnt-evil-just-misunderstood
vitmalina
@vitmalina Aus Zakas 'Artikel: "Dies kann gefährlich sein, wenn Sie Benutzereingaben über eval () ausführen. Wenn Ihre Eingaben jedoch nicht vom Benutzer stammen, besteht dann eine echte Gefahr?" Das ist genau das Problem. Sobald Ihr Code über die Proportionen von "Hallo Welt" hinaus wächst, ist es schnell unmöglich zu beweisen, dass Sie keine Benutzereingaben verlieren eval. In jeder seriösen Webanwendung mit mehreren Mandanten, in der Dutzende von Entwicklern an derselben Codebasis arbeiten, ist dies nicht akzeptabel.
Ruud Helderman