Was bedeutet "unsachgemäße Verwendung" der Javascript Eval-Funktion? [geschlossen]

13

Eval ist ein notorisch umstrittenes Sprachmerkmal. Douglas Crockford lehnt es rundweg ab. Ich frage mich, welche spezifischen Risiken Eval mit sich bringt. Nach dieser Frage Improper use of eval opens up your code for injection attacks.

Was sind einige Missbräuche des Eval-Befehls und welche Sicherheitslücken eröffnen sie?

Derek Adair
quelle

Antworten:

31

Als ich die JScript-Engine implementiert habe, habe ich mich dafür ausgesprochen , EVAL IS EVIL- Shirts bedrucken zu lassen, aber leider sind wir nie dazu gekommen.

Mein größtes Problem mit eval ist nicht der offensichtliche Angriff durch böswillige Code-Injektion, obwohl dies mit Sicherheit enorme Bedenken hervorruft. Mein größtes Problem dabei ist, dass die Leute es als einen wirklich großen Hammer benutzen, um wirklich kleine Probleme zu lösen. Die meisten realen Verwendungen, die ich im JScript-Team von "eval" in the wild gesehen habe, konnten mithilfe einer Nachschlagetabelle trivial gelöst werden. und da jedes Objekt in JScript bereits eine Nachschlagetabelle ist , war dies keine lästige Belastung. Eval startet den Compiler erneut und zerstört vollständig die Fähigkeit des Compilers, Ihren Code zu optimieren .

Weitere Gedanken in diesem Sinne finden Sie in meinen Artikeln aus dem Jahr 2003 zum Thema:

Allgemeine Übelkeit:

http://blogs.msdn.com/b/ericlippert/archive/2003/11/01/53329.aspx

Injection Attack Böse:

http://blogs.msdn.com/b/ericlippert/archive/2003/11/04/53335.aspx

Eric Lippert
quelle
Was halten Sie davon, evalgroße Codestücke so zu erstellen, wie es Anwendungen wie JSPacker tun? JSPacker + gzip führt normalerweise zu kleineren Dateigrößen als jede der beiden Lösungen für sich. Wie Sie jedoch zu Recht betonen, wird ein Compiler im Wesentlichen zweimal mit demselben Code gestartet.
Matthew Scharley
2
@Matthew: Es gibt oft einen Kompromiss zwischen Raum und Zeit, und das Ausnutzen dieses Kompromisses kann manchmal ein großer Gewinn sein. Wenn der Zweck der Technik darin besteht, die Leistung zu verbessern, dann ist meine Meinung, dass wenn eine sorgfältige Messung zeigt, dass dies in realistischen Szenarien ohne Einführung von Sicherheitslücken ein bedeutender Gewinn ist, großartig, tun Sie das. Aber ich weiß nicht genug über die spezifische Technik, um deren Details zu kritisieren.
Eric Lippert
Ich wünsche eine der Antworten , die ich gesehen hatte , entweder hier oder auf SO würde sich Ihre zweite Verbindungszustände festgestellt haben , was: das eval()ist nicht ein Sicherheitsrisiko auf den Client (Browser) Code.
sq33G
4

Bei den meisten Sicherheitslücken handelt es sich um die gleiche Art von Lücken wie bei der SQL-Injektion, nämlich um die Verkettung von Benutzereingaben in JavaScript-Code. Der Unterschied besteht darin, dass es zwar Möglichkeiten gibt, um sicherzustellen, dass dies mit SQL nicht geschieht, mit JavaScript jedoch nicht viel zu tun ist.

Als triviales und nutzloses Beispiel ein simpler JavaScript-Rechner:

textbox1.value = eval(textbox2.value);

Ein korrektes Anwendungsbeispiel sind einige der JavaScript-Packer, die JavaScript komprimieren, indem sie gebräuchliche Wörter herausnehmen und durch kurze Ersetzungen von 1 bis 2 Zeichen ersetzen. Der Packer gibt dann all dies zusammen mit dem String-Ersetzungscode aus, der auf dem generierten Wörterbuch basiert, und wertet dann das Ergebnis aus.

Matthew Scharley
quelle
1

Es gibt einige Dinge , die in JS zu tun , ohne eine eval-ähnlichen Funktion (unmöglich sind eval, Functionund vielleicht auch mehr).

Nimm applyzum Beispiel. Es ist einfach zu bedienen, wenn es für gewöhnliche Funktionsaufrufe verwendet wird:

foo.apply(null, [a, b, c])

Aber wie würden Sie dies für Objekte tun, die Sie mithilfe einer neuen Syntax erstellen?

new Foo.apply(null, [a, b, c]) funktioniert nicht und macht auch keine ähnlichen Formen.

Sie können diese Einschränkung jedoch mit evaloder umgehen Function(ich verwende Functionin diesem Beispiel):

Function.prototype.New = (function () {
    var fs = [];
    return function () {
        var f = fs[arguments.length];
        if (f) {
            return f.apply(this, arguments);
        }
        var argStrs = [];
        for (var i = 0; i < arguments.length; ++i) {
            argStrs.push("a[" + i + "]");
        }
        f = new Function("var a=arguments;return new this(" + argStrs.join() + ");");
        if (arguments.length < 100) {
            fs[arguments.length] = f;
        }
        return f.apply(this, arguments);
    };
}) ();

Beispiel:

Foo.New.apply(null, [a, b, c]);

Natürlich können Sie die Funktionen, die Function.prototype.Newverwendet werden, manuell erstellen , aber dies ist nicht nur ausführlich und klobig, es muss (per Definition) endlich sein. Functionermöglicht es dem Code, für eine beliebige Anzahl von Argumenten zu arbeiten.

Thomas Eding
quelle
2
Stimmt, aber die Frage des OP lautete: " Was sind einige unzulässige Verwendungen des Eval-Befehls und welche Sicherheitslücken eröffnen sie? ", Nicht " Wovon ist eine gute Verwendung eval()? "
Ross Patterson
1
@ RossPatterson: Vermutlich habe ich mir hauptsächlich den Titel der Frage angesehen, haha.
Thomas Eding
Trotzdem +1 für die Suche nach einer guten Verwendung für ein schlechtes Sprachfeature :-)
Ross Patterson
1

Eine etwas direkte Antwort meinerseits war die Entwicklung eines entwicklerorientierten API-Testbereichs. Wir gaben einen Textbereich auf einer Seite und eine Schaltfläche Ausführen. Der zentrale Punkt der Seite war, dass sie in der Lage waren, Dinge in Javascript mit unserer iframe-kommunizierenden API auszuprobieren, die in einer lokalen Umgebung nicht so einfach waren.

Alles, was in diesem Fall böswillig wäre, hätte auch der Entwickler tun können, der seine F12-Tools geöffnet hat.

Katana314
quelle
0

Ich bin damit einverstanden, dass es sehr selten verwendet werden sollte, aber ich habe einen leistungsstarken Anwendungsfall für gefunden eval.

In Firefox gibt es eine experimentelle neue Funktion namens asm.js, mit der ich gearbeitet habe. Dadurch kann eine begrenzte Teilmenge der Javascript-Sprache in nativen Code kompiliert werden. Ja, das ist großartig, aber es gibt Einschränkungen. Sie können sich die begrenzte Teilmenge von Javascript als eine C-ähnliche Sprache vorstellen, die in Javascript eingebettet ist. Es ist nicht wirklich dazu gedacht, von Menschen gelesen oder geschrieben zu werden.

Diese eingeschränkte Untermenge von Javascript ermöglicht es mir nicht, meinen zur Laufzeit generierten Code in den kompilierten Code einzufügen, sobald dieser kompiliert wurde.

Ich habe einen Code geschrieben, mit dem ein Benutzer in vertrauter Schreibweise einen mathematischen Ausdruck schreiben und ihn sofort in asm.js-Code konvertieren kann. Sofern ich den Code nicht auf dem Server verarbeiten lassen möchte (was ich nicht möchte), evalist dies das einzige Tool, mit dem ich den resultierenden Code vom Browser in Echtzeit verarbeiten lassen kann.

Reismehl Cookies
quelle
0

Wie von anderen hervorgehoben, ist das Wichtigste bei der Arbeit eval, es sicher zu halten. Zu diesem Zweck möchten Sie eine gründliche Argumentprüfung durchführen und den evalCode einfach halten , da es im Allgemeinen sehr viel schwieriger ist, zur Laufzeit generierten Code zu verwalten und zu sichern.

Davon abgesehen genieße ich es, evalfür zwei Arten von Dingen zu verwenden (auch wenn es wahrscheinlich bessere, weniger böse Alternativen gibt):

  1. Da evales sich um eine Methode zum "expliziten Zwischenspeichern von Code" handelt, kann sie zur Verbesserung der Leistung verwendet werden. Beachten Sie, dass Optimierer ständig verbessert werden, es jedoch keine Garantie dafür gibt, was sie für Sie tun können. Indem Sie Dinge im Code explizit machen, können Sie dem Optimierer tatsächlich dabei helfen, intelligentere Entscheidungen zu treffen.
  2. Es kann auch verwendet werden, um grundlegende Formen der Typensicherheit sowie andere Sprachfunktionen bereitzustellen, die JS nicht bietet, ohne die Leistung zu beeinträchtigen.

Dieser Ansatz für vorkompilierte Objektiteratoren zeigt beispielsweise deutliche Leistungsvorteile bei der Verwendung evalfür die Iteration von Objekteigenschaften. Es zeigt auch die Anfänge eines leistungsstarken Typsystems, das eine implizite Einschränkungsprüfung und vieles mehr zu geringen oder keinen Kosten bereitstellen kann.

Viele erfahrene Javascript-Entwickler würden wahrscheinlich darauf hinweisen, dass dies ein Kaninchenbau ist, denn wenn Sie anfangen, Javascript auf diese Weise zu schreiben, ändern Sie im Wesentlichen die Art und Weise, wie Sie die Sprache verwenden. Dies ist jedoch möglicherweise nicht unbedingt eine schlechte Sache für diejenigen, die Javascript mögen, aber es fehlen auch grundlegende Sprachfunktionen, die nur durch eine Änderung der Art und Weise, wie wir die Sprache selbst verwenden, erreicht werden können.

Domi
quelle
-3

Ich habe eine Situation, in der eval wie der Weg aussieht, eine Zeichenfolge auszuwerten und eine vorhandene Variable zurückzugeben, deren Name mit der Zeichenfolge identisch ist.

Ich habe mehrere Tabellen: table1ResultsTable, table2ResultsTable, tableNResultsTable. Ich habe Variablen mit den gleichen Namen eingerichtet, die jQuery-datierbare Objekte sind. Ich benutze diese, um die Tabellen einzurichten und mit jQuery datierbare Funktionen aufzurufen.

Jeder Tabelle ist class = "resultsTable" zugewiesen. Jetzt muss ich die Variable abrufen, wenn auf die Tabelle geklickt wird. Ich mache das:

$('resultsTable).on('click', 'td', function(event) {
    var cResultsTable = eval(event.target.parentElement.parentElement.parentElement.id);
    [etc]
});

Ich erhalte also die ID der Tabelle, in der die Zelle angeklickt wurde, die denselben Namen hat wie die zugehörige datierbare Objektvariable. Wenn jemand einen Verbesserungsvorschlag hat, würde ich gerne davon erfahren.

BobRodes
quelle
1
Warum brauchst du eval? Die ID sollte eine einfache Zeichenfolge sein, kein Code. Jedenfalls event.target.parentElement.parentElement.parentElementist schrecklich. Außerdem würde ich erwarten, dass der Aufruf von eval den Fehler "Referenzfehler: [ID] ist nicht definiert" generiert, es sei denn, Sie verwenden absichtlich auswertbare IDs (die fehlerhaft und falsch sind und möglicherweise zu seltsamen Ereignissen führen, wenn Sie aufhören doppelte IDs erzeugen).
Brian
Vielleicht mache ich mich nicht klar. Die ID ist eine einfache Zeichenfolge. Es ist die ID der Tabelle, auf die geklickt wurde. Ich habe auch eine Objektvariable (festgelegt mit der Methode jQuery datatable (), die auf dieselbe Tabelle verweist) mit demselben Namen wie die ID. Ich versuche, diese Variable abzurufen, um auf ihre Funktionen zuzugreifen (tatsächlich, um die Klasse "row_selected" zur ausgewählten Zeile hinzuzufügen), wenn auf die Tabelle geklickt wird. Seit ich das geschrieben habe, habe ich jedoch eine Verbesserung gefunden. Ich füge einfach alle Objektreferenzen in ein Array ein, benenne die Elemente so wie die ID und stecke die ID-Zeichenfolge in diese, um die Objektreferenz zu erhalten.
BobRodes
Brian, wenn Sie die ID der Tabelle, auf die geklickt wurde, besser finden können, bin ich ganz Ohr. Für die Datatables-Funktion muss ich das click -Ereignis für die Zelle verarbeiten und die row_selected-Klasse zu ihrem parentNode hinzufügen. Warum ich das Zeilenereignis nicht einfach verarbeiten und die Klasse direkt hinzufügen kann, weiß ich nicht, aber die Zeile wird dabei nicht als ausgewählt angezeigt.
BobRodes
1
Vielleicht mache ich mich nicht klar. Wenn Sie eval für eine einfache Zeichenfolge (dh eine Zeichenfolge, die nicht von JS stammt) aufrufen, werden Sie eine Ausnahme auslösen. Daher macht Ihre Verwendung von eval keinen Sinn. Wenn Sie zufällig eine Objektvariable mit dem gleichen Namen wie die ID generieren, funktioniert Ihr Code ... scheint mir jedoch defekt zu sein. Ich würde die Variable lieber mit erstellen document.getElementById(ID).MySpecialProperty = MYSPECIALPROPERTYVALUE(um nicht zu sagen, dass das auch großartig ist, aber es ist besser als eval). Eric Lippert betont: "Jedes Objekt in JScript ist bereits eine Nachschlagetabelle."
Brian
Ok, Sie sagen also, Sie möchten der Elementreferenz eine Eigenschaft hinzufügen und sie auf die Referenz des datierbaren Objekts setzen? Das klingt für mich auch enger.
BobRodes