it-swarm-eu.dev

Proč je použití funkce JavaScript eval špatný nápad?

Funkce eval je výkonný a snadný způsob, jak dynamicky generovat kód, takže jaké jsou námitky?

503
Brian Singh
  1. Nesprávné použití eval otevře váš kód pro injekční útoky

  2. Ladění může být náročnější (žádná čísla linek atd.)

  3. eval'd kód se spustí pomaleji (bez možnosti kompilace/cache eval'd kódu)

Edit: Jak @Jeff Walden připomíná v komentářích, # 3 je dnes méně pravdivý, než tomu bylo v roce 2008. Přestože se ale může stát, že se může stát, že se některé kompilace skriptů uloží do mezipaměti, bude to omezeno pouze na skripty, které jsou opakovány beze změny. Pravděpodobnější scénář je, že vyhodíte skripty, které pokaždé prošly mírnou úpravou a jako takové nemohly být uloženy do mezipaměti. Řekněme, že NĚKTERÝ evald kód se spouští pomaleji.

369
Prestaul

eval není vždy zlý. Jsou chvíle, kdy je to naprosto vhodné.

Eval je však v současnosti a historicky masivně využíván lidmi, kteří nevědí, co dělají. To zahrnuje, bohužel, lidi, kteří píší JavaScriptové návody, av některých případech to může mít bezpečnostní důsledky - nebo častěji jednoduché chyby. Čím více tedy můžeme udělat, aby se hodil otazník nad Evalem, tím lépe. Kdykoli použijete eval, musíte zkontrolovat rozumně to, co děláte, protože je pravděpodobné, že byste to mohli dělat lépe, bezpečněji a čistěji.

Chcete-li uvést příliš typický příklad, nastavit barvu prvku s id uloženým v proměnné „potato“:

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

Pokud by autoři výše uvedeného kódu měli ponětí o základech toho, jak fungují objekty JavaScriptu, uvědomili by si, že místo doslovných tečkových jmen lze použít hranaté závorky, čímž se vyhýbá potřebě eval:

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

... což je mnohem snáze čitelné a méně potenciálně buggy.

(Ale pak někdo, kdo/opravdu/věděl, co dělá, by řekl:

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

což je spolehlivější než podivný starý trik přístupu k prvkům DOM přímo z objektu dokumentu.)

343
bobince

Věřím, že je to proto, že dokáže provádět jakoukoli funkci JavaScriptu z řetězce. Jeho použití usnadňuje lidem vkládání nepoctivých kódů do aplikace.

36
kemiller2002

Přicházejí na mysl dva body:

  1. Zabezpečení (ale pokud vygenerujete řetězec, který chcete vyhodnotit sami, může to být problém)

  2. Výkon: dokud není kód, který má být spuštěn, neznámý, nelze jej optimalizovat. (o javascriptu a výkonu, určitě prezentace Steve Yegge )

26
xtofl

Předání vstupu uživatele do eval () představuje bezpečnostní riziko, ale také každé vyvolání eval () vytvoří novou instanci interpretu JavaScriptu. To může být prase zdrojů.

20
Andrew Hedges

Obecně se jedná pouze o problém, pokud předáváte vstup uživatele eval.

17
Mark Biek

Údržba a ladění je mnohem těžší. Je to jako goto. Můžete ji použít, ale je obtížnější najít problémy a těžší pro lidi, kteří budou možná muset později provést změny.

15
Brian

Nezapomeňte na to, že eval () můžete často používat ke spuštění kódu v jinak omezeném prostředí - weby sociálních sítí, které blokují specifické funkce JavaScriptu, mohou být někdy zmateny jejich rozdělením do bloku eval -

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

Pokud tedy chcete spustit nějaký kód JavaScript, kde by to jinak nebylo povoleno ( Myspace , dívám se na vás ...), pak může být eval () užitečným trikem.

Avšak ze všech výše uvedených důvodů byste jej neměli používat pro svůj vlastní kód, kde máte úplnou kontrolu - prostě to není nutné a lépe odsunuto do police „složitých hacků JavaScriptu“.

13
matt lohkamp

Pokud necháte eval () dynamický obsah (pomocí cgi nebo vstupu), je stejně bezpečný a solidní jako všechny ostatní JavaScript na vaší stránce.

11
Thevs

Spolu se zbytkem odpovědí si nemyslím, že výrazy eval mohou mít pokročilou minimalizaci.

7
Paul Mendoza

Je to možné bezpečnostní riziko, má jiný rozsah provádění a je docela neefektivní, protože vytváří zcela nové skriptovací prostředí pro provádění kódu. Více informací najdete zde: eval .

Je to docela užitečné, a když se používá s moderováním, může přidat spoustu dobrých funkcí.

6
Tom

Vím, že tato diskuse je stará, ale opravdu se mi líbí tento přístup společnosti Google a chtěl jsem se s tímto pocitem podělit s ostatními;)

Druhou věcí je, že čím lépe, tím více se snažíte porozumět a nakonec prostě nevěříte, že něco je dobré nebo špatné jen proto, že to někdo řekl :) Toto je velmi inspirativní video , které mi pomohlo myslet více na sebe :) DOBRÉ POSTUPY jsou dobré, ale nepoužívejte je bezdůvodně :)

5
op1ekun

Pokud si nejste stoprocentně jisti, že vyhodnocovaný kód pochází z důvěryhodného zdroje (obvykle z vaší vlastní aplikace), pak je to jistý způsob, jak vystavit váš systém skriptovacímu útoku mezi weby.

5
John Topley

Není to nutně tak špatné, pokud víte, v jakém kontextu jej používáte.

Pokud vaše aplikace používá eval() k vytvoření objektu z nějakého JSON, který se vrátil z XMLHttpRequest na váš vlastní web vytvořený důvěryhodným kódem na straně serveru, pravděpodobně to není problém.

Nedůvěryhodný kód JavaScriptu na straně klienta to stejně nemůže udělat. Pokud věc, kterou provádíte eval() on, přišla z rozumného zdroje, jste v pořádku.

5
MarkR

Výrazně snižuje vaši důvěru v zabezpečení.

4
David Plumpton

Pokud chcete, aby uživatel zadal některé logické funkce a vyhodnotil AND A OR, je funkce eval JavaScriptu perfektní. Mohu přijmout dva řetězce a eval(uate) string1 === string2 atd.

4
Ian

Pokud v kódu zjistíte použití eval (), pamatujte, že mantra „eval () je zlá.“

Tato funkce vezme libovolný řetězec a spustí jej jako kód JavaScript. Pokud je dotyčný kód znám předem (není určen za běhu), není důvod používat eval (). Pokud je kód dynamicky generován za běhu, často existuje lepší způsob, jak dosáhnout cíle bez eval (). Například pouhé použití zápisu s hranatými závorkami pro přístup k dynamickým vlastnostem je lepší a jednodušší:

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

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

Používání eval() má také bezpečnostní důsledky, protože můžete provádět kód (například pocházející ze sítě), se kterým bylo manipulováno. Toto je běžný protichůdný prostředek při řešení odpovědi JSON z požadavku Ajaxu. V těchto případech je lepší použít vestavěné metody prohlížeče k analýze odpovědi JSON, abyste se ujistili, že je bezpečná a platná. V prohlížečích, které nativně nepodporují JSON.parse(), můžete použít knihovnu z JSON.org.

Je také důležité si uvědomit, že předávání řetězců setInterval(), setTimeout() a konstruktoru Function() je z větší části podobné použití eval(), a proto by se mu mělo zabránit.

Za scénami musí JavaScript stále vyhodnotit a spustit řetězec, který předáte jako programový kód:

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

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

Použití nového konstruktoru Function () je podobné eval () a mělo by se s ním zacházet opatrně. Mohl by to být silný konstrukt, ale je často zneužíván. Pokud absolutně musíte použít eval(), můžete místo toho použít novou funkci Function ().

Existuje malá potenciální výhoda, protože kód vyhodnocený v nové funkci () bude spuštěn v rozsahu lokálních funkcí, takže jakékoli proměnné definované v proměnném v hodnoceném kódu nebudou automaticky globály.

Dalším způsobem, jak zabránit automatickým globálům, je zabalit volání eval() do okamžité funkce.

3
12345678

Kromě možných problémů se zabezpečením, pokud provádíte kód odeslaný uživatelem, je většinou lepší způsob, který nezahrnuje opětovnou analýzu kódu při každém jeho spuštění. Anonymní funkce nebo vlastnosti objektu mohou nahradit většinu použití evalu a jsou mnohem bezpečnější a rychlejší.

2
Matthew Crumley

To se může stát problémem, protože nová generace prohlížečů vyjde s nějakou chutí kompilátoru JavaScriptu. Kód spuštěný pomocí Eval nemusí fungovat tak dobře jako zbytek vašeho JavaScriptu proti těmto novějším prohlížečům. Někdo by měl udělat nějaké profilování.

2
Brian

eval () je velmi silný a lze jej použít k provedení příkazu JS nebo k vyhodnocení výrazu. Otázka však není o použití eval (), ale řekněme jen, jak je řetězec, který spouštíte s eval (), ovlivněn škodlivou stranou. Nakonec spustíte škodlivý kód. S mocí přichází velká odpovědnost. Takže je používejte moudře, pokud jej používáte. To moc nesouvisí s funkcí eval (), ale tento článek obsahuje docela dobré informace: http://blogs.popart.com/2009/07/javascript-injection-attacks/ Pokud hledáte základy eval () najdete zde: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval

2
Phi

Toto je jeden z dobrých článků o eval a jak to není zlo: http://www.nczonline.net/blog/2013/06/25/eval-isnt-evil-just-misunderstood/

Neříkám, že byste měli vyběhnout a začít používat eval () všude. Ve skutečnosti existuje velmi málo dobrých příkladů použití pro spuštění eval (). Určitě existují obavy ohledně srozumitelnosti kódu, laditelnosti a určitě výkonu, který by neměl být přehlížen. Neměli byste se však bát používat, když máte případ, kdy má eval () smysl. Zkuste to nejdříve nepoužít, ale nenechte se nikoho vyděsit, aby si mysleli, že váš kód je křehčí nebo méně bezpečný, pokud je eval () používán správně.

2
Amr Elgarhy

Není to vždycky špatný nápad. Vezměme si například generování kódu. Nedávno jsem napsal knihovnu s názvem Hyperbars , která překlenuje mezeru mezi virtual-dom a řídítka . To se provádí analýzou šablony řídítek a jejím převodem na hyperscript , který je následně používán virtuální doménou. Hyperscript je generován jako řetězec první a před jeho vrácením, eval(), aby jej proměnil v spustitelný kód. Našel jsem eval() v této konkrétní situaci přesný opak zla.

V podstatě z

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

K tomuto

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

Výkon funkce eval() není v takové situaci problém, protože vygenerovaný řetězec potřebujete interpretovat pouze jednou a pak znovu použít spustitelný výstup mnohokrát.

Můžete vidět, jak bylo generování kódu dosaženo, pokud jste zvědaví zde .

1
Wikened

Nebudu se snažit vyvrátit nic, co bylo doposud řečeno, ale nabídnu toto použití eval (), které (pokud vím) nemůže být provedeno jiným způsobem. Pravděpodobně existují i ​​jiné způsoby, jak to kódovat, a pravděpodobně i způsoby, jak jej optimalizovat, ale je to provedeno dlouho a bez zvonků a píšťalek pro přehlednost, aby se ilustrovalo použití evalu, který ve skutečnosti nemá žádné jiné alternativy. To je: dynamické (nebo přesněji) programově vytvořené názvy objektů (na rozdíl od hodnot).

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

ÚPRAVA: Mimochodem, nenavrhuji (ze všech bezpečnostních důvodů, na které jsem poukázal), že byste založili jména objektů na vstupu uživatele. Nedokážu si však představit žádný dobrý důvod, proč byste to chtěli udělat. Přesto si myslel, že bych to zdůraznil, že by to nebyl dobrý nápad :)

1
Carnix

Chtěl bych říct, že nezáleží na tom, jestli použijete eval() v javascriptu, který je spuštěn v prohlížečích. * (Upozornění)

Všechny moderní prohlížeče mají vývojovou konzolu, kde můžete i tak provádět libovolný javascript a jakýkoli polointeligentní vývojář se může podívat na váš zdroj JS a vložit do konzole dev cokoli, co potřebuje, aby udělal, co chce.

* Pokud mají vaše koncové body serveru správnou validaci a dezinfekci hodnot dodaných uživatelem, nemělo by na tom, co bude analyzováno a vyhodnoceno v javascriptu na straně klienta.

Pokud byste se měli zeptat, zda je vhodné použít eval() v PHP, odpověď je NO, pokud whitelist nějaké hodnoty, které mohou být předán vašemu evalskému prohlášení.

1
Adam Copley

JavaScript Engine má řadu optimalizací výkonu, které provádí během fáze kompilace. Některé z nich se scvrkávají na to, že jsou schopny v podstatě staticky analyzovat kód, když lexují, a předem určit, kde jsou všechny deklarace proměnných a funkcí, takže trvá méně úsilí k vyřešení identifikátorů během provádění.

Pokud ale Engine najde v kódu eval (..), musí v podstatě předpokládat, že veškeré jeho povědomí o umístění identifikátoru může být neplatné, protože v lexingovém čase nemůže přesně vědět, jaký kód můžete předat eval (..) k úpravě lexikálního oboru nebo obsahu objektu, se kterým můžete předat, za účelem vytvoření nového lexikálního oboru, který bude konzultován.

Jinými slovy, v pesimistickém smyslu je většina těch optimalizací, které by provedla, zbytečná, pokud bude přítomen eval (..), takže optimalizace vůbec neprovádí.

To vše vysvětluje.

Reference:

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