Perché utilizzare la funzione eval JavaScript è una cattiva idea?



Answers

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

Tuttavia, eval è attualmente e storicamente eccessivamente utilizzato da persone che non sanno cosa stanno facendo. Ciò include persone che scrivono tutorial JavaScript, sfortunatamente, e in alcuni casi questo può davvero 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 di accesso agli elementi DOM direttamente dall'oggetto del documento).

Question

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




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.




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-misunderstood/

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 (). Sicuramente ci sono problemi con la chiarezza del codice, la debugabilità e certamente 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 per primo, 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.




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




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

Questa funzione prende una stringa arbitraria e la esegue come codice JavaScript. Quando il codice in questione è noto in anticipo (non determinato in fase di esecuzione), non c'è motivo di utilizzare eval (). Se il codice viene generato dinamicamente in fase di esecuzione, c'è spesso un modo migliore per raggiungere l'obiettivo senza eval (). Ad esempio, solo l'uso della notazione con parentesi quadra 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 è meglio 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() modo nativo, puoi utilizzare una libreria da JSON.org.

È anche importante ricordare che passare le stringhe a setInterval() , setTimeout() e al 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 di Function () è simile a eval () e dovrebbe essere affrontato con attenzione. Potrebbe essere un potente costrutto, ma viene spesso utilizzato in modo improprio. Se si deve assolutamente usare eval() , si può prendere in considerazione l'uso 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à globals automaticamente.

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




Una cosa da tenere a mente è che spesso è possibile utilizzare eval () per eseguire il codice in un ambiente altrimenti limitato: i siti di social networking 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 permesso ( Myspace , ti sto guardando ...) allora 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 è necessario, e preferibilmente retrocesso allo scaffale degli "ingannevoli javascript JavaScript".




Riduce notevolmente il tuo livello di sicurezza sulla sicurezza.




Non tenterò di confutare qualunque 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 in precedenza) di basare i nomi degli oggetti sull'input dell'utente. Non riesco a immaginare nessuna buona ragione per cui tu voglia farlo. Eppure, pensavo che avrei fatto notare che non sarebbe stata una buona idea :)




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 esattamente al codice tempo che 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




Due punti vengono in mente:

  1. Sicurezza (ma fino a quando si genera la stringa da valutare da sé, questo potrebbe essere un non-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 )




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ù sicuri e veloci.




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




Related