JavaScript eval()
Wie eval() in JavaScript einen string als Code ausführt: Syntax, Rückgabewert, direkte und indirekte Aufrufe, Sicherheits- und Leistungsrisiken sowie Alternativen.
JavaScript kann zur Laufzeit einen string in ausführbaren Code umwandeln. Die eingebaute Funktion eval() tut genau das: Sie nimmt einen string, behandelt ihn als JavaScript-Quellcode und führt ihn aus. Diese Seite erklärt, wie eval() funktioniert, wann sie einen Wert zurückgibt, warum sie fast immer das falsche Werkzeug ist und was stattdessen verwendet werden sollte.
Diese Seite behandelt:
- Die Syntax von
eval()und was sie zurückgibt. - Praktische Anwendungsfälle (und warum die meisten davon heute bessere Alternativen haben).
- Die Sicherheits- und Leistungsprobleme, die
eval()gefährlich machen. - Sicherere Alternativen: der
Function-Konstruktor undJSON.parse(). - Best Practices für die seltenen Fälle, in denen man
eval()tatsächlich benötigt.
Die eval()-Funktion verstehen
eval() wertet einen string mit JavaScript-Code aus oder führt ihn aus. Ist der string ein Ausdruck, wertet eval() ihn aus und gibt das Ergebnis zurück. Enthält der string eine oder mehrere Anweisungen, führt eval() sie aus und gibt den Wert der letzten Ausdrucksanweisung zurück (oder undefined).
Syntax
eval(string)Parameter:
string— ein string mit JavaScript-Quellcode, der ausgewertet werden soll.
Rückgabewert: das Ergebnis des zuletzt ausgewerteten Ausdrucks oder undefined, wenn der string keine Ausdrücke enthält.
Ist string kein string (z. B. eine Zahl), gibt eval() das Argument unverändert zurück.
Verwendungsbeispiel
In diesem Beispiel wird ein einfacher arithmetischer Ausdruck ausgewertet und das Ergebnis ausgegeben:
Praktische Anwendungsfälle für eval()
Obwohl die Funktion eval() sehr mächtig sein kann, wird ihre Verwendung aufgrund von Sicherheits- und Leistungsproblemen oft abgeraten. Es gibt jedoch Szenarien, in denen eval() nützlich sein kann.
Dynamische Codeausführung
In Situationen, in denen Code dynamisch generiert und ausgeführt werden muss, kann eval() eine praktische Lösung sein. Zum Beispiel beim Erstellen eines Taschenrechners, der Benutzereingaben als mathematischen Ausdruck auswertet:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Dynamic Calculator</title>
</head>
<body>
<p>Add any arithmetic expression like 12 + 5 and hit 'Calculate' button!</p>
<div>
<input type="text" id="expression" placeholder="Enter expression" /> <!-- User input field -->
<button onclick="evaluateExpression()">Calculate</button> <!-- Button to trigger calculation -->
</div>
<div id="calcResult"></div> <!-- Element to display result -->
<script>
function evaluateExpression() {
const expression = document.getElementById('expression').value; // Get user input
try {
const result = eval(expression); // Evaluate the expression
document.getElementById('calcResult').textContent = `Result: ${result}`; // Display the result
} catch (e) {
document.getElementById('calcResult').textContent = 'Invalid expression'; // Handle invalid input
}
}
</script>
</body>
</html>JSON mit eval() parsen
Vor der Einführung von JSON.parse() wurde eval() zum Parsen von JSON-strings verwendet. Aus Sicherheitsgründen wird jedoch heute die Verwendung von JSON.parse() empfohlen.
Bei der Verwendung von eval() zum Parsen von JSON-strings ist es entscheidend sicherzustellen, dass der JSON-string als object-Ausdruck und nicht als Blockanweisung interpretiert wird. Dazu wird der JSON-string in Klammern eingeschlossen, bevor er an eval() übergeben wird. Dies stellt sicher, dass JavaScript den string als object-Ausdruck behandelt und Syntaxfehler vermieden werden, die entstehen könnten, wenn der string als Blockanweisung interpretiert würde.
Bevorzuge immer JSON.parse() gegenüber eval() zum Parsen von JSON. JSON.parse() akzeptiert nur Daten und kann daher niemals ein eingeschleustes Skript ausführen.
Direkter vs. indirekter eval und Scope
Wie eval() Variablen sieht, hängt davon ab, wie man es aufruft.
Ein direkter Aufruf — eval(code) wörtlich geschrieben — führt den Code im aktuellen Scope aus. Er kann Variablen lesen und sogar dort erstellen, wo er aufgerufen wird:
Ein indirekter Aufruf — wenn eval über eine andere Referenz als den wörtlichen Namen aufgerufen wird, z. B. (0, eval)(code) oder durch Speichern in einer Variable — führt den Code stattdessen im globalen Scope aus. Er kann keine lokalen Variablen sehen:
Im Strict Mode erhält sogar ein direktes eval() seinen eigenen Scope: Variablen, die es deklariert, gelangen nicht in die umgebende Funktion. Das ist ein weiterer Grund, warum moderner Code eval() kaum noch benötigt.
Sicherheits- und Leistungsbedenken
Die Verwendung von eval() kann erhebliche Sicherheitsrisiken und Leistungsprobleme verursachen. Hier ist der Grund dafür:
Sicherheitsrisiken
eval() kann beliebigen JavaScript-Code ausführen und ist daher anfällig für Injection-Angriffe. Ein Angreifer könnte potenziell schadhaften Code einschleusen, was zu schwerwiegenden Sicherheitsverletzungen führen kann.
Beispiel für ein Sicherheitsrisiko:
Stell dir vor, du hast eine Webanwendung, die Benutzereingaben entgegennimmt und mit eval() auswertet. Ohne ordnungsgemäße Validierung kann dies von einem Angreifer ausgenutzt werden, um schadhaften Code auszuführen.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>eval() Security Risk Example</title>
</head>
<body>
<div>
<p>Hit 'Run Code' button!</p>
<input type="text" id="userInput" placeholder="Enter code" value="alert('Hacked!')" /> <!-- User input field -->
<button onclick="evaluateUserInput()">Run Code</button> <!-- Button to run code -->
</div>
<div id="userInputResult"></div> <!-- Element to display result -->
<script>
function evaluateUserInput() {
const input = document.getElementById('userInput').value; // Get user input
try {
const result = eval(input); // Evaluate the user input
document.getElementById('userInputResult').textContent = `Result: ${result}`; // Display the result
} catch (e) {
document.getElementById('userInputResult').textContent = 'Error in evaluation'; // Handle evaluation error
}
}
</script>
</body>
</html>In diesem Beispiel führt eval() eine Eingabe wie alert('Hacked!') aus und verursacht damit eine Sicherheitsverletzung durch Anzeige eines Alert-Fensters. Ein Angreifer könnte schädlicheren Code einschleusen und potenziell das gesamte System kompromittieren.
Leistungsprobleme
eval() führt Code im selben Scope aus, von dem aus es aufgerufen wird, und verhindert damit bestimmte Optimierungen der JavaScript-Engine. Dies kann im Vergleich zu anderen Ansätzen zu langsamerer Leistung führen.
Beispiel für ein Leistungsproblem:
Betrachten wir ein Szenario, in dem wir eine Reihe von Berechnungen mehrfach durchführen müssen. Die Verwendung von eval() für diese Aufgabe kann die Leistung erheblich beeinträchtigen.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Performance Issues with eval()</title>
</head>
<body>
<div id="evalResult"></div>
<div id="functionResult"></div>
<script>
// Using eval()
const evalStartTime = performance.now();
for (let i = 0; i < 100000; i++) {
eval("3 * 4 + 5");
}
const evalEndTime = performance.now();
const evalDuration = evalEndTime - evalStartTime;
document.getElementById('evalResult').textContent = `Time taken with eval(): ${evalDuration} ms`;
// Using Function constructor
const func = new Function('return 3 * 4 + 5');
const funcStartTime = performance.now();
for (let i = 0; i < 100000; i++) {
func();
}
const funcEndTime = performance.now();
const funcDuration = funcEndTime - funcStartTime;
document.getElementById('functionResult').textContent = `Time taken with Function constructor: ${funcDuration} ms`;
</script>
</body>
</html>In diesem Beispiel vergleichen wir die Leistung von eval() mit dem Function-Konstruktor. Wir führen eine einfache arithmetische Operation (3 * 4 + 5) 100.000 Mal mit beiden Methoden aus und messen die jeweilige Dauer.
- Mit
eval(): Der Code innerhalb voneval()wird wiederholt ausgewertet, was langsamer ist, da bestimmte Optimierungen verhindert werden. - Mit dem
Function-Konstruktor: DerFunction-Konstruktor erstellt eine Funktion, die dieselbe Operation ausführt, aber schneller ist, da er JavaScript-Engines erlaubt, die wiederholte Ausführung zu optimieren.
Die auf der Webseite angezeigten Ergebnisse zeigen die von jeder Methode benötigte Zeit und demonstrieren, dass eval() aufgrund seiner Leistungsprobleme langsamer ist.
Vermeide eval(), es sei denn, es ist absolut notwendig. Greife zuerst auf sicherere Alternativen zurück: den Function-Konstruktor für dynamische Funktionen oder JSON.parse() für Daten.
Alternativen zu eval()
Für eine sicherere und effizientere Codeausführung sollten folgende Alternativen in Betracht gezogen werden:
Verwendung des Function-Konstruktors
Der Function-Konstruktor erstellt ein neues Funktionsobjekt, ähnlich wie eval(), aber mit besserer Sicherheit und Leistung.
Verwendung von JSON.parse()
Zum Parsen von JSON-strings ist JSON.parse() die bevorzugte Methode.
Best Practices für die Verwendung von eval()
Wenn eval() unbedingt verwendet werden muss, sollten diese Best Practices befolgt werden, um Risiken zu minimieren:
1. Eingaben validieren
Stelle sicher, dass die Eingabe für eval() streng validiert wird, um Code-Injection zu verhindern. Das bedeutet, nur sichere Zeichen und Ausdrücke zuzulassen.
Beispiel:
In diesem Beispiel wird ein regulärer Ausdruck verwendet, um die Benutzereingabe zu validieren. Nur numerische Zeichen und grundlegende arithmetische Operatoren sind erlaubt. Stimmt die Eingabe mit dem Muster überein, wird sie mit eval() ausgewertet. Andernfalls wird sie als ungültig abgelehnt.
2. Try-Catch verwenden
Umschließe eval() mit einem Try-Catch-Block, um potenzielle Fehler elegant zu behandeln. Dies verhindert, dass die gesamte Anwendung abstürzt, wenn eval() auf einen Fehler trifft.
Beispiel:
Hier werden Syntaxfehler in der Benutzereingabe mit einem Try-Catch-Block behandelt. Wirft eval() einen Fehler, wird er abgefangen und eine Fehlermeldung ausgegeben, anstatt die Anwendung zum Absturz zu bringen.
3. Scope einschränken
Minimiere den Scope der für eval() zugänglichen Variablen, um potenzielle Schäden durch schadhaften Code zu begrenzen. Verwende einen sofort aufgerufenen Funktionsausdruck (IIFE), um einen lokalen Scope zu erstellen.
Beispiel:
In diesem Beispiel wird eine Funktion restrictedEval innerhalb eines IIFE definiert, um einen lokalen Scope zu erstellen. Diese Funktion wertet die Eingabe mit eval() aus, aber mit lokal definierten Variablen a und b. Die globalen Variablen a und b sind nicht zugänglich, was die Scope-Einschränkung demonstriert.
Durch die Einhaltung dieser Best Practices können die mit der Verwendung von eval() verbundenen Risiken verringert werden, während seine dynamischen Codeausführungsfähigkeiten bei Bedarf weiterhin genutzt werden können.
Fazit
Die Funktion eval() in JavaScript bietet ein mächtiges Werkzeug für die dynamische Codeausführung, ist aber mit erheblichen Risiken und Leistungsnachteilen verbunden. In der Praxis gibt es für fast jede Aufgabe, die eval() zu erfordern scheint, eine sicherere Lösung: Verwende JSON.parse() für Daten, den Function-Konstruktor für dynamische Funktionen und object-/array-Lookups anstelle von aus strings zusammengesetzten Variablennamen. Musst du eval() verwenden, validiere die Eingaben, umschließe es mit try...catch und halte seinen Scope so klein wie möglich.
Verwandte Themen
- Die "new Function"-Syntax — der sicherere Weg, eine Funktion aus einem string zu erstellen.
- Arbeiten mit JSON —
JSON.parse()undJSON.stringify(). - Fehlerbehandlung: try...catch — Fehler abfangen, die von ausgewertetem Code geworfen werden.
- Strict Mode: "use strict" — wie der Strict Mode den Scope von
eval()verändert.