it-swarm.it

Perché usare la funzione eval JavaScript è un'idea cattiva?

La funzione eval è un modo semplice e potente per generare dinamicamente il codice, quindi quali sono i caveat?

503
Brian Singh
  1. L'uso improprio di eval apre il tuo codice per gli attacchi di iniezione

  2. Il debugging può essere più impegnativo (nessun numero di linea, ecc.)

  3. il codice eval'd viene eseguito più lentamente (nessuna possibilità di compilare/memorizzare in cache il codice eval'd)

Edit: Come @Jeff Walden fa notare nei commenti, # 3 è meno vero oggi di quanto non fosse nel 2008. Tuttavia, mentre alcuni caching di script compilati possono accadere, ciò sarà limitato agli script che sono stati eval'd ripetuti senza alcuna modifica. Uno scenario più probabile è che si stanno valutando script che hanno subito ogni volta piccole modifiche e che quindi non possono essere memorizzati nella cache. Diciamo solo che QUALSIASI codice eval'd viene eseguito più lentamente.

369
Prestaul

la valutazione non è sempre cattiva. Ci sono momenti in cui è perfettamente appropriato.

Tuttavia, la valutazione è attualmente e storicamente eccessivamente utilizzata da persone che non sanno cosa stanno facendo. Ciò include persone che scrivono tutorial JavaScript, sfortunatamente, e in alcuni casi questo può effettivamente avere conseguenze sulla sicurezza o, più spesso, semplici bug. Quindi più possiamo fare per gettare un punto interrogativo su eval, meglio è. Ogni volta che usi eval hai bisogno di controllare il tuo operato, perché è probabile che tu possa farlo in un modo migliore, più sicuro e più pulito.

Per dare un esempio troppo tipico, per impostare il colore di un elemento con un ID memorizzato nella variabile "potato":

eval('document.' + potato + '.style.color = "red"');

Se gli autori del tipo di codice precedente avessero un indizio sulle basi del funzionamento degli oggetti JavaScript, si sarebbero resi conto che è possibile utilizzare parentesi quadre invece di nomi punto letterali, evitando la necessità di eval:

document[potato].style.color = 'red';

... che è molto più facile da leggere e meno potenzialmente buggy.

(Ma poi, qualcuno che/davvero/sapeva cosa stavano facendo avrebbe detto:

document.getElementById(potato).style.color = 'red';

che è più affidabile del vecchio trucco malizioso di accedere agli elementi DOM direttamente dall'oggetto del documento).

343
bobince

Credo che sia perché può eseguire qualsiasi funzione JavaScript da una stringa. Usarlo rende più facile per le persone iniettare il codice rogue nell'applicazione.

36
kemiller2002

Due punti vengono in mente:

  1. Sicurezza (ma se si genera la stringa da valutare autonomamente, potrebbe trattarsi di un problema)

  2. Prestazioni: fino a quando il codice da eseguire è sconosciuto, non può essere ottimizzato. (su javascript e performance, certamente la presentazione di Steve Yegge )

26
xtofl

Passare l'input dell'utente a eval () è un rischio per la sicurezza, ma anche ogni chiamata di eval () crea una nuova istanza dell'interprete JavaScript. Questo può essere un vantaggio per le risorse.

20
Andrew Hedges

Solitamente è un problema solo se si passa l'input dell'utente eval.

17
Mark Biek

Soprattutto, è molto più difficile da mantenere e eseguire il debug. È come un goto. Puoi usarlo, ma rende più difficile trovare problemi e più difficile con le persone che potrebbero aver bisogno di apportare modifiche in seguito.

15
Brian

Una cosa da tenere a mente è che spesso è possibile utilizzare eval () per eseguire codice in un ambiente altrimenti limitato: i siti di social network che bloccano specifiche funzioni JavaScript possono a volte essere ingannati rompendoli in un blocco eval -

eval('al' + 'er' + 't(\'' + 'hi there!' + '\')');

Quindi, se stai cercando di eseguire qualche codice JavaScript dove altrimenti non potrebbe essere consentito ( Myspace , ti sto guardando ...) quindi eval () può essere un trucco utile.

Tuttavia, per tutti i motivi sopra menzionati, non dovresti usarlo per il tuo codice personale, in cui hai il controllo completo: non è solo necessario, e meglio è retrocesso allo scaffale degli "ingannevoli javascript JavaScript".

13
matt lohkamp

A meno che tu non abbia reso eval () un contenuto dinamico (tramite cgi o input), è sicuro e solido come tutti gli altri JavaScript nella tua pagina.

11
Thevs

Insieme al resto delle risposte, non penso che le dichiarazioni di valutazione possano avere una minimizzazione avanzata.

7
Paul Mendoza

È un possibile rischio per la sicurezza, ha un diverso ambito di esecuzione ed è piuttosto inefficiente, poiché crea un ambiente di scripting completamente nuovo per l'esecuzione del codice. Vedi qui per ulteriori informazioni: eval .

È abbastanza utile, tuttavia, e usato con moderazione può aggiungere molte buone funzionalità.

6
Tom

So che questa discussione è vecchia, ma mi piace molto questo approccio di Google e volevo condividere questo sentimento con gli altri;)

L'altra cosa è che meglio si ottiene più si tenta di capire e infine non si crede che qualcosa sia buono o cattivo solo perché qualcuno lo ha detto :) Questo è molto stimolante video che mi ha aiutato a pensare di più da solo :) BUONE PRATICHE sono buone, ma non usarle irrimediabilmente :)

5
op1ekun

A meno che tu non sia sicuro al 100% che il codice che viene valutato provenga da una fonte attendibile (solitamente la tua applicazione), allora è un modo sicuro per esporre il tuo sistema a un attacco di scripting cross-site.

5
John Topley

Non è necessariamente così male purché tu sappia in quale contesto lo stai utilizzando.

Se la tua applicazione sta usando eval() per creare un oggetto da qualche JSON che è tornato da un XMLHttpRequest al tuo sito, creato dal tuo server di fiducia codice, probabilmente non è un problema.

Il codice JavaScript lato client non attendibile non può comunque fare altrettanto. A condizione che la cosa che stai eseguendo eval() on provenga da una fonte ragionevole, stai bene.

5
MarkR

Riduce notevolmente il tuo livello di sicurezza sulla sicurezza.

4
David Plumpton

Se si desidera che l'utente inserisca alcune funzioni logiche e valuti per AND il OR, la funzione eval JavaScript è perfetta. Posso accettare due stringhe e eval(uate) string1 === string2, ecc.

4
Ian

Se noti l'uso di eval () nel tuo codice, ricorda il mantra "eval () è malvagio".

Questa funzione accetta una stringa arbitraria e la esegue come codice JavaScript. Quando il codice in questione è conosciuto in anticipo (non determinato in fase di runtime), non c'è motivo di utilizzare eval (). Se il codice viene generato dinamicamente in fase di runtime, c'è spesso un modo migliore per raggiungere l'obiettivo senza eval (). Ad esempio, solo l'uso della notazione a parentesi quadre per accedere alle proprietà dinamiche è migliore e più semplice:

// antipattern
var property = "name";
alert(eval("obj." + property));

// preferred
var property = "name";
alert(obj[property]);

L'uso di eval() ha anche implicazioni sulla sicurezza, perché potresti eseguire codice (ad esempio proveniente dalla rete) che è stato manomesso. Questo è un antipattern comune quando si ha a che fare con una risposta JSON da una richiesta Ajax. In questi casi è preferibile utilizzare i metodi incorporati dei browser per analizzare la risposta JSON per assicurarsi che sia sicura e valida. Per i browser che non supportano JSON.parse() in modo nativo, puoi utilizzare una libreria da JSON.org.

È anche importante ricordare che passare le stringhe a setInterval(), setTimeout() e il costruttore Function() è, per la maggior parte, simile all'utilizzo di eval() e quindi dovrebbe essere evitato.

Dietro le quinte, JavaScript deve ancora valutare ed eseguire la stringa passata come codice di programmazione:

// antipatterns
setTimeout("myFunc()", 1000);
setTimeout("myFunc(1, 2, 3)", 1000);

// preferred
setTimeout(myFunc, 1000);
setTimeout(function () {
myFunc(1, 2, 3);
}, 1000);

L'utilizzo del nuovo costruttore Function () è simile a eval () e dovrebbe essere affrontato con attenzione. Potrebbe essere un potente costrutto, ma viene spesso usato in modo improprio. Se devi assolutamente usare eval(), puoi considerare l'utilizzo della nuova funzione ().

C'è un piccolo vantaggio potenziale perché il codice valutato nella nuova funzione Function () verrà eseguito in un ambito di funzione locale, quindi qualsiasi variabile definita con var nel codice valutato non diventerà globalmente automaticamente.

Un altro modo per evitare globali automatici è quello di avvolgere la chiamata eval() in una funzione immediata.

3
12345678

Oltre ai possibili problemi di sicurezza se si sta eseguendo il codice inviato dall'utente, la maggior parte delle volte c'è un modo migliore che non comporta la ri-analisi del codice ogni volta che viene eseguito. Le funzioni anonime o le proprietà degli oggetti possono sostituire la maggior parte degli usi di eval e sono molto più sicure e veloci.

2
Matthew Crumley

Questo potrebbe diventare più di un problema in quanto la prossima generazione di browser uscirà con il sapore di un compilatore JavaScript. Il codice eseguito tramite Eval potrebbe non funzionare come il resto del tuo JavaScript contro questi nuovi browser. Qualcuno dovrebbe fare un po 'di profilazione.

2
Brian

eval () è molto potente e può essere utilizzato per eseguire un'istruzione JS o valutare un'espressione. Ma la domanda non riguarda gli usi di eval (), ma lascia solo dire come la stringa che stai usando con eval () sia influenzata da una parte malintenzionata. Alla fine eseguirai un codice malevolo. Con il potere derivano grandi responsabilità. Quindi usalo saggiamente se lo stai usando. Questo non è molto correlato alla funzione eval () ma questo articolo ha delle buone informazioni: http://blogs.popart.com/2009/07/javascript-injection-attacks/ Se stai cercando le basi di eval () guarda qui: https://developer.mozilla.org/en-US/docs/Web/JavaScript/ Riferimento/Global_Objects/eval

2
Phi

Questo è uno dei buoni articoli che parlano di eval e di come non sia un male: http://www.nczonline.net/blog/2013/06/25/eval-isnt-evil- just-frainteso/

Non sto dicendo che dovresti andare a correre fuori e iniziare a usare eval () ovunque. In effetti, esistono pochissimi buoni casi d'uso per l'esecuzione di eval (). Ci sono sicuramente problemi con la chiarezza del codice, la debugabilità e sicuramente le prestazioni che non dovrebbero essere trascurate. Ma non dovresti aver paura di usarlo quando hai un caso in cui eval () ha senso. Prova a non usarlo prima, ma non lasciare che nessuno ti spaventi a pensare che il tuo codice sia più fragile o meno sicuro quando eval () viene usato in modo appropriato.

2
Amr Elgarhy

Non è sempre una cattiva idea. Prendiamo ad esempio la generazione del codice. Recentemente ho scritto una libreria chiamata Hyperbars che colma il divario tra virtual-dom e manubri . Lo fa analizzando un modello di handlebars e convertendolo in hyperscript che viene successivamente utilizzato da virtual-dom. L'iperscript viene generato come stringa e prima di restituirlo, eval() it per trasformarlo in codice eseguibile. Ho trovato eval() in questa particolare situazione l'esatto opposto del male.

Fondamentalmente da

<div>
    {{#each names}}
        <span>{{this}}</span>
    {{/each}}
</div>

A questo

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

Le prestazioni di eval() non sono un problema in una situazione come questa, perché è sufficiente interpretare la stringa generata una sola volta e quindi riutilizzare l'output eseguibile più volte.

Puoi vedere come è stata raggiunta la generazione del codice se sei curioso qui .

1
Wikened

Non tenterò di confutare qualsiasi cosa sia stata detta finora, ma offrirò questo uso di eval () che (per quanto ne so) non può essere fatto in altro modo. Ci sono probabilmente altri modi per codificarlo, e probabilmente modi per ottimizzarlo, ma questo è fatto a mano e senza campane e fischietti per chiarezza per illustrare un uso di eval che in realtà non ha altre alternative. Ovvero: nomi di oggetti dinamici (o più precisi) creati a livello di programmazione (in contrapposizione ai valori).

//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);

EDIT: a proposito, non suggerirei (per tutti i motivi di sicurezza indicati precedentemente) che si basino i nomi degli oggetti sull'input dell'utente. Non riesco a immaginare nessuna buona ragione per cui tu voglia farlo. Comunque, ho pensato che avrei fatto notare che non sarebbe stata una buona idea :)

1
Carnix

Vorrei dire che non ha molta importanza se si utilizza eval() in javascript che viene eseguito nei browser. * (Avvertenza)

Tutti i browser moderni dispongono di una console per sviluppatori in cui è comunque possibile eseguire javascript arbitrari e qualsiasi sviluppatore semi-intelligente può guardare la sorgente JS e inserire tutti i bit di cui ha bisogno nella console dev per fare ciò che desidera.

* Finché gli endpoint del tuo server hanno la validazione e la sanificazione corrette dei valori forniti dall'utente, non dovrebbe importare ciò che viene analizzato e valutato nel javascript del tuo client.

Se dovessi chiedere se è appropriato usare eval() in PHP tuttavia, la risposta è NO, a meno che tu whitelist qualsiasi valore che può essere passato alla tua dichiarazione di valutazione.

1
Adam Copley

Il motore JavaScript ha un numero di ottimizzazioni delle prestazioni che viene eseguito durante la fase di compilazione. Alcuni di questi si riducono ad essere in grado di analizzare essenzialmente in modo statico il codice mentre esegue il lex e predeterminare dove sono presenti tutte le dichiarazioni di variabili e funzioni, in modo che sia necessario uno sforzo minore per risolvere gli identificatori durante l'esecuzione.

Ma se il Motore trova una valutazione (..) nel codice, in sostanza deve presumere che tutta la sua consapevolezza della posizione dell'identificatore potrebbe non essere valida, perché non può sapere al momento del lexing esattamente quale codice si può passare a eval (..) per modificare lo scope lessicale o il contenuto dell'oggetto che si può passare a creare un nuovo scope lessicale da consultare.

In altre parole, in senso pessimistico, la maggior parte di quelle ottimizzazioni che farebbe sono inutili se eval (..) è presente, quindi semplicemente non esegue affatto le ottimizzazioni.

Questo spiega tutto.

Riferimento:

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

1
hkasera