JavaScript eval()
Scopri come eval() in JavaScript esegue una stringa come codice: sintassi, valore restituito, chiamate dirette e indirette, rischi di sicurezza e alternative più sicure come il costruttore Function e JSON.parse().
JavaScript può trasformare una stringa in codice eseguibile a runtime. La funzione integrata eval() fa esattamente questo: riceve una stringa, la tratta come sorgente JavaScript e la esegue. Questa pagina spiega come funziona eval(), quando restituisce un valore, perché è quasi sempre lo strumento sbagliato e quali alternative usare al suo posto.
Questa pagina tratta:
- La sintassi di
eval()e cosa restituisce. - Casi d'uso reali (e perché la maggior parte di essi ha alternative migliori oggi).
- I problemi di sicurezza e di prestazioni che rendono
eval()pericoloso. - Alternative più sicure: il costruttore
FunctioneJSON.parse(). - Le buone pratiche per i rari casi in cui è davvero necessario.
Comprendere la funzione eval()
eval() valuta o esegue una stringa di codice JavaScript. Se la stringa è un'espressione, eval() la valuta e restituisce il risultato. Se la stringa contiene una o più istruzioni, eval() le esegue e restituisce il valore dell'ultima istruzione espressione (o undefined).
Sintassi
eval(string)Parametri:
string— una stringa di codice sorgente JavaScript da valutare.
Valore restituito: il risultato dell'ultima espressione valutata, o undefined se la stringa non contiene espressioni.
Se string non è una stringa (ad esempio un numero), eval() restituisce l'argomento invariato.
Esempio di utilizzo
In questo esempio valutiamo una semplice espressione aritmetica e stampiamo il risultato:
Casi d'uso pratici di eval()
Sebbene la funzione eval() possa essere estremamente potente, il suo utilizzo è spesso sconsigliato per motivi di sicurezza e prestazioni. Tuttavia, esistono scenari in cui eval() può essere utile.
Esecuzione dinamica del codice
Nelle situazioni in cui il codice deve essere generato ed eseguito dinamicamente, eval() può essere una soluzione pratica. Ad esempio, la creazione di una calcolatrice che valuta l'input dell'utente come espressione matematica:
<!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>Parsing di JSON con eval()
Prima dell'introduzione di JSON.parse(), eval() veniva utilizzato per analizzare le stringhe JSON. Tuttavia, ora si raccomanda di usare JSON.parse() per motivi di sicurezza.
Quando si usa eval() per analizzare stringhe JSON, è fondamentale assicurarsi che la stringa JSON venga interpretata come un'espressione object anziché come un blocco di istruzioni. Per ottenere questo risultato, la stringa JSON viene racchiusa tra parentesi prima di essere passata a eval(). Ciò garantisce che JavaScript tratti la stringa come un'espressione object, prevenendo gli errori di sintassi che potrebbero verificarsi se la stringa venisse interpretata come un blocco di istruzioni.
Preferisci sempre JSON.parse() rispetto a eval() per analizzare il JSON. JSON.parse() accetta solo dati, quindi non potrà mai eseguire uno script iniettato.
eval() diretto vs. indiretto e scope
Il modo in cui eval() vede le variabili dipende da come lo chiami.
Una chiamata diretta — eval(code) scritto letteralmente — esegue il codice nello scope corrente. Può leggere e persino creare variabili nel punto in cui viene chiamato:
Una chiamata indiretta — richiamare eval attraverso qualsiasi riferimento diverso dal nome letterale, come (0, eval)(code) o memorizzandolo in una variabile — esegue il codice nello scope globale. Non può vedere le variabili locali:
In strict mode, anche un eval() diretto ottiene il proprio scope: le variabili che dichiara non fuoriescono nella funzione circostante. Questo è un ulteriore motivo per cui il codice moderno raramente ha bisogno di eval().
Problemi di sicurezza e prestazioni
L'uso di eval() può comportare significativi rischi di sicurezza e problemi di prestazioni. Ecco perché:
Rischi di sicurezza
eval() può eseguire qualsiasi codice JavaScript, rendendolo vulnerabile agli attacchi di tipo injection. Un aggressore potrebbe iniettare codice malevolo, causando gravi violazioni della sicurezza.
Esempio di rischio di sicurezza:
Immagina di avere un'applicazione web che riceve input dall'utente e lo valuta con eval(). Senza una corretta validazione, questo può essere sfruttato da un aggressore per eseguire codice malevolo.
<!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 questo esempio, se un utente inserisce qualcosa come alert('Hacked!'), la funzione eval() lo eseguirà, causando una violazione della sicurezza mostrando una finestra di avviso. Un aggressore potrebbe iniettare codice ancora più dannoso, compromettendo potenzialmente l'intero sistema.
Problemi di prestazioni
eval() esegue il codice nello stesso scope dal quale viene chiamato, ostacolando le ottimizzazioni del motore JavaScript. Questo può portare a prestazioni più lente rispetto ad altri approcci.
Esempio di problema di prestazioni:
Consideriamo uno scenario in cui dobbiamo eseguire una serie di calcoli più volte. Usare eval() per questo compito può influire significativamente sulle prestazioni.
<!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 questo esempio, confrontiamo le prestazioni di eval() con il costruttore Function. Eseguiamo una semplice operazione aritmetica (3 * 4 + 5) 100.000 volte con entrambi i metodi e misuriamo il tempo impiegato da ciascuno.
- Usando
eval(): Il codice all'interno dieval()viene valutato ripetutamente, il che è più lento perché impedisce alcune ottimizzazioni. - Usando il costruttore
Function: Il costruttoreFunctioncrea una funzione che esegue la stessa operazione, ma è più veloce perché consente ai motori JavaScript di ottimizzare l'esecuzione ripetuta.
I risultati visualizzati sulla pagina web mostrano il tempo impiegato da ciascun metodo, dimostrando che eval() è più lento a causa dei suoi problemi di prestazioni.
Evita eval() a meno che non sia assolutamente necessario. Opta prima per alternative più sicure: il costruttore Function per funzioni dinamiche, o JSON.parse() per i dati.
Alternative a eval()
Per un'esecuzione del codice più sicura ed efficiente, considera le seguenti alternative:
Uso del costruttore Function
Il costruttore Function crea un nuovo object funzione, simile a eval(), ma con maggiore sicurezza e prestazioni migliori.
Uso di JSON.parse()
Per analizzare le stringhe JSON, JSON.parse() è il metodo preferito.
Buone pratiche per l'uso di eval()
Se devi usare eval(), segui queste buone pratiche per ridurre i rischi:
1. Valida l'input
Assicurati che l'input passato a eval() sia rigorosamente validato per prevenire l'iniezione di codice. Ciò significa consentire solo caratteri ed espressioni sicuri.
Esempio:
In questo esempio, utilizziamo un'espressione regolare per validare l'input dell'utente. Sono consentiti solo caratteri numerici e operatori aritmetici di base. Se l'input corrisponde al pattern, viene valutato con eval(). Altrimenti, viene rifiutato come non valido.
2. Usa Try-Catch
Racchiudi eval() in un blocco try-catch per gestire eventuali errori in modo controllato. Questo impedisce all'intera applicazione di bloccarsi se eval() incontra un errore.
Esempio:
Qui gestiamo gli errori di sintassi nell'input dell'utente usando un blocco try-catch. Se eval() lancia un errore, viene intercettato e viene registrato un messaggio di errore invece di bloccare l'applicazione.
3. Limita lo scope
Riduci al minimo lo scope delle variabili accessibili a eval() per limitare i potenziali danni da codice malevolo. Usa un'espressione di funzione immediatamente invocata (IIFE) per creare uno scope locale.
Esempio:
In questo esempio, definiamo una funzione restrictedEval all'interno di una IIFE per creare uno scope locale. Questa funzione valuta l'input usando eval() ma con variabili a e b definite localmente. Le variabili globali a e b non sono accessibili, dimostrando la restrizione dello scope.
Seguendo queste buone pratiche, puoi ridurre i rischi associati all'uso di eval() pur sfruttando le sue capacità di esecuzione dinamica del codice quando necessario.
Conclusione
La funzione eval() in JavaScript offre uno strumento potente per l'esecuzione dinamica del codice, ma comporta rischi significativi e svantaggi in termini di prestazioni. In pratica, quasi qualsiasi attività che sembra richiedere eval() ha una soluzione più sicura: usa JSON.parse() per i dati, il costruttore Function per le funzioni dinamiche, e lookup su object/array invece di costruire nomi di variabili da stringhe. Se devi usare eval(), valida l'input, racchiudilo in try...catch, e mantieni il suo scope il più piccolo possibile.
Argomenti correlati
- La sintassi "new Function" — il modo più sicuro per costruire una funzione da una stringa.
- Lavorare con JSON —
JSON.parse()eJSON.stringify(). - Gestione degli errori: try...catch — catturare gli errori lanciati dal codice valutato.
- Strict mode: "use strict" — come lo strict mode modifica lo scope di
eval().