Kiedy JavaScript eval () nie jest zły?


263

Piszę trochę kodu JavaScript do analizy funkcji wprowadzonych przez użytkownika (dla funkcji podobnych do arkusza kalkulacyjnego). Po przeanalizowaniu formuły mogłem przekonwertować ją na JavaScript i uruchomić eval()na niej, aby uzyskać wynik.

Jednak zawsze unikałem używania, eval()jeśli mogę tego uniknąć, ponieważ jest to złe (i słusznie lub nie, zawsze myślałem, że w JavaScript jest to jeszcze bardziej złe, ponieważ oceniany kod może zostać zmieniony przez użytkownika ).

Więc kiedy można z niego korzystać?


5
Większość bibliotek JSON nie używa eval pod maską, aby chronić się przed zagrożeniami bezpieczeństwa.
Sean McMillan,

11
@Sean - Zarówno ewaluacja JQuery, jak i prototypu (JQuery korzysta z nowej funkcji)
plodder

5
@plodder - Skąd czerpiesz informacje? jQuery korzysta z natywnej JSON.parse () od wersji 1.4 (jeszcze w 1/2010)! Zobacz sam: code.jquery.com/jquery-1.4.js
Ken

3
„Oczywiście trzeba użyć eval () do parsowania JSON” - nie jest to prawda, wręcz przeciwnie - nie należy używać eval do parsowania JSON! Użyj skryptu json2.js Douglasa Crockfordsa (twórcy JSON) z json.org !
TMS,

11
@Tom ironia polega na tym, że json2.js używa eval do parsowania JSON
tobyodavies

Odpowiedzi:


262

Chciałbym poświęcić chwilę na zajęcie się przesłanką twojego pytania - że eval () jest „ złe ”. Słowo „ zło ”, używane przez ludzi posługujących się językiem programowania, zwykle oznacza „niebezpieczne”, a ściślej „zdolne do spowodowania wielu szkód za pomocą prostego polecenia”. Kiedy więc można używać czegoś niebezpiecznego? Kiedy wiesz, jakie jest niebezpieczeństwo i kiedy podejmujesz odpowiednie środki ostrożności.

Do rzeczy, spójrzmy na niebezpieczeństwa związane z używaniem eval (). Prawdopodobnie istnieje wiele małych ukrytych zagrożeń, tak jak wszystko inne, ale dwa duże zagrożenia - powód, dla którego eval () jest uważany za zły - to wydajność i wstrzykiwanie kodu.

  • Wydajność - eval () uruchamia interpreter / kompilator. Jeśli Twój kod jest skompilowany, jest to duży hit, ponieważ musisz wywołać potencjalnie ciężki kompilator w czasie wykonywania. Jednak JavaScript jest nadal w większości językiem interpretowanym, co oznacza, że ​​wywołanie eval () nie jest dużym spadkiem wydajności w ogólnym przypadku (ale zobacz moje szczegółowe uwagi poniżej).
  • Wstrzyknięcie kodu - eval () potencjalnie uruchamia ciąg kodu z podwyższonymi uprawnieniami. Na przykład program działający jako administrator / root nigdy nie chciałby ewaluować danych wejściowych użytkownika, ponieważ dane te mogłyby potencjalnie mieć wartość „rm -rf / etc / important-file” lub gorsze. Ponownie, JavaScript w przeglądarce nie ma tego problemu, ponieważ program i tak działa na koncie użytkownika. JavaScript po stronie serwera może mieć ten problem.

Do konkretnego przypadku. Z tego, co rozumiem, generujesz ciągi samodzielnie, więc zakładając, że uważasz, aby nie dopuścić do wygenerowania ciągu takiego jak „rm -rf coś ważnego”, nie ma ryzyka wstrzyknięcia kodu (ale pamiętaj, to bardzo, bardzo trudno to zapewnić w ogólnym przypadku). Ponadto, jeśli działasz w przeglądarce, wstrzyknięcie kodu jest dość niewielkim ryzykiem, jak sądzę.

Jeśli chodzi o wydajność, będziesz musiał zważyć to z łatwością kodowania. Moim zdaniem, jeśli analizujesz formułę, równie dobrze możesz obliczyć wynik podczas analizy, zamiast uruchamiać inny analizator składni (ten wewnątrz eval ()). Jednak kodowanie za pomocą eval () może być łatwiejsze, a uderzenie wydajności prawdopodobnie będzie niezauważalne. Wygląda na to, że eval () w tym przypadku nie jest bardziej zły niż jakakolwiek inna funkcja, która mogłaby zaoszczędzić trochę czasu.


78
Nie rozwiązujesz problemu kodu, w którym eval jest trudny do debugowania
bobobobo

48
Wtrysk kodu jest bardzo poważnym problemem dla javascript, jeśli w ogóle martwisz się danymi użytkownika. Wstrzyknięty kod będzie działał (w przeglądarce) tak, jakby pochodził z Twojej witryny, pozwalając mu wykonać dowolny rodzaj shenanigan, który użytkownik może wykonać ręcznie. Jeśli zezwolisz (innemu) kodowi wejść na twoją stronę, może on zamawiać rzeczy w imieniu klienta, zmieniać ich gravatar lub cokolwiek, co mogliby zrobić za pośrednictwem Twojej witryny. Bądź bardzo ostrożny. Pozwalanie hakerom na posiadanie klientów jest tak samo złe, jak pozwalanie im na posiadanie twojego serwera.
Sean McMillan,

71
Jeśli dane pochodzą z twojego serwera i coś, co Ty wygenerowałeś, programista nie szkodzi przy użyciu eval (). Prawdziwą szkodą jest wiara we wszystko, co czytasz. Widzisz wielu ludzi, którzy mówią, że eval () jest złem i nie mają pojęcia, dlaczego, oprócz tego, że gdzieś to przeczytali.
Vince Panuccio,

42
@Sean McMillan: Chcę ci wierzyć, ale jeśli ktoś będzie przechwytywał i zmieniał skrypt javascript na eval()twoim serwerze, może również po prostu zmienić źródło strony, a także przejąć kontrolę nad informacjami użytkownika. . . Nie widzę różnicy.
Walt W

20
Odnośnie „Wstrzyknięcie kodu - ... Znowu JavaScript w przeglądarce nie ma tego problemu” i „Ponadto, jeśli działasz w przeglądarce, wstrzyknięcie kodu jest dość niewielkim ryzykiem, jak sądzę”. Czy sugerujesz, że wstrzyknięcie kodu w przeglądarce nie stanowi problemu? XSS znajduje się w pierwszej trójce na liście 10 najlepszych OWASP od kilku lat.
Mike Samuel,

72

eval()nie jest zły. Lub, jeśli tak, to zło w taki sam sposób, jak odbicie, we / wy pliku / sieci, wątki i IPC są „złe” w innych językach.

Jeśli, Twoim zdaniem , eval()jest on szybszy niż interpretacja ręczna lub upraszczasz kod, lub jest bardziej przejrzysty ... powinieneś go użyć. Jeśli nie, to nie powinieneś. Proste.


5
Jednym z takich celów może być generowanie zoptymalizowanego kodu, który byłby albo zbyt długi, albo zbyt powtarzalny, aby pisać ręcznie. Takie rzeczy, które w LISP wymagałyby makra.
wberry

5
Jest to tak ogólna rada, że ​​można ją zastosować do dosłownie każdego istniejącego bloku kodu. To naprawdę nie dodaje niczego do tego pytania; w szczególności nie pomaga nikomu tu przyjść ustalić, czy ich użycie jest problematyczne, czy nie.
jpmc26

2
Szybciej, prościej, wyraźniej ... Ta odpowiedź nie obejmuje wystarczająco dobrze implikacji bezpieczeństwa.
Ruud Helderman

55

Gdy ufasz źródłu.

W przypadku JSON jest mniej lub bardziej trudno manipulować źródłem, ponieważ pochodzi on z kontrolowanego serwera WWW. Tak długo, jak sam JSON nie zawiera danych przesłanych przez użytkownika, nie ma poważnej wady korzystania z eval.

We wszystkich innych przypadkach dokładałbym wszelkich starań, aby upewnić się, że dane dostarczone przez użytkownika są zgodne z moimi regułami przed przekazaniem ich do eval ().


13
Ciąg json należy zawsze przetestować pod kątem gramatyki json przed użyciem go w eval (). Zatem ciąg json „{foo: alert ('XSS')} 'nie przejdzie, ponieważ„ alert (' XSS ') ”nie jest poprawną wartością.
Gumbo,

3
W takim razie skorzystaj z HTTPS. OTOH: man-in-the-middle nie jest typowym scenariuszem ataku dla aplikacji internetowej do odmian ogrodniczych, podczas gdy np. Jest to skryptowanie między witrynami.
Tomalak,

7
evalnie będzie również poprawnie analizować wszystkich prawidłowych ciągów JSON. Na przykład JSON.parse(' "\u2028" ') === "\u2028", ale eval(' "\u2028" ')zgłasza wyjątek, ponieważ U + 2028 jest znak nowej linii w JavaScript, ale nie jest to znak nowej linii miarę JSON jest zaniepokojony.
Mike Samuel,

1
@Justin - jeśli protokół jest zagrożony, zazwyczaj początkowe ładowanie strony zostałoby wysłane za pomocą tego samego protokołu, a następnie jest to kwestia sporna, ponieważ klient jest już tak zagrożony, jak to możliwe.
antinome,

1
Pięknie powiedziane @Tomalak, wspomniałem o tym teraz w mojej odpowiedzi! Niesamowite!
NiCk Newman

25

Zdobądźmy prawdziwych ludzi:

  1. Każda większa przeglądarka ma teraz wbudowaną konsolę, z której potencjalny haker może korzystać w obfitości, aby wywoływać dowolną funkcję o dowolnej wartości - dlaczego mieliby zawracać sobie głowę użyciem instrukcji eval - nawet gdyby mogli?

  2. Jeśli skompilowanie 2000 wierszy JavaScript zajmuje 0,2 sekundy, jaki jest mój spadek wydajności, jeśli wyewaluuję cztery wiersze JSON?

Nawet wyjaśnienie Crockforda „eval is evil” jest słabe.

eval is Evil, funkcja eval jest najczęściej wykorzystywaną funkcją JavaScript. Unikaj tego

Jak sam Crockford mógłby powiedzieć: „Takie oświadczenie zwykle powoduje irracjonalną nerwicę. Nie kupuj tego”.

Zrozumienie ewaluacji i wiedza, kiedy może być przydatne, jest o wiele ważniejsze. Na przykład eval to rozsądne narzędzie do oceny odpowiedzi serwera wygenerowanych przez twoje oprogramowanie.

BTW: Prototype.js wywołuje eval bezpośrednio pięć razy (w tym w evalJSON () i evalResponse ()). jQuery używa go w parseJSON (przez konstruktor funkcji).


10
JQuery korzysta z wbudowanej funkcji JSON.parse przeglądarki, jeśli jest dostępna (która jest znacznie szybsza i bezpieczniejsza), używając eval tylko jako mechanizmu awaryjnego. Stwierdzenie „eval is evil” jest dość dobrą wytyczną.
jjmontes

30
Re „Każda większa przeglądarka ma teraz wbudowaną konsolę ...”. Wstrzykiwanie kodu jest problemem, gdy jeden użytkownik może wprowadzić kod, który jest następnie uruchamiany w przeglądarce innego użytkownika. Konsole przeglądarki same w sobie nie pozwalają jednemu użytkownikowi uruchamiać kodu w przeglądarce innego użytkownika, więc nie mają znaczenia przy podejmowaniu decyzji, czy warto zabezpieczyć się przed wstrzyknięciem kodu.
Mike Samuel

28
„Każda duża przeglądarka ma teraz wbudowaną konsolę ... dlaczego mieliby zadawać sobie trud korzystania z instrukcji eval? - Jesteś daleko od celu. Sugeruję edycję odpowiedzi. Ważnym problemem jest zdolność jednego użytkownika do wstrzykiwania kodu, który można uruchomić w przeglądarce innego użytkownika. I właśnie tam musisz stać się naprawdę prawdziwy.
akkishore

5
@akkishore, będę wdzięczny, jeśli wymyślisz prawdziwy przykład z życia, który popiera twoje stwierdzenia.
Akash Kava,

7
@AkashKava Nie zdajesz sobie sprawy, że jeśli prześlę javascript w polu komentarza, a javascript dostanie się do bazy danych. Kiedy inny użytkownik wyświetli ten komentarz (do którego wstawiam javascript), eval weźmie javascript podczas jego renderowania i oceni go za pomocą interpretera, powodując uruchomienie mojego osadzonego javascript w przeglądarce innego użytkownika. W ten sposób mogę zebrać najróżniejsze informacje. Ich nazwa użytkownika, identyfikator użytkownika w bazie danych, adres e-mail itp. To nie jest trudna odpowiedź, gdybyś miał Googled XSS, zobaczysz za około 10 sekund, dlaczego jest to problem.
Kyle Richter,

18

I mają tendencję do naśladowania porady Crockforda jest za eval(), a uniknąć jej całkowicie. Nawet sposoby, które wydają się tego wymagać, nie wymagają tego. Na przykład setTimeout()pozwala przekazać funkcję zamiast ewaluować.

setTimeout(function() {
  alert('hi');
}, 1000);

Nawet jeśli jest to zaufane źródło, nie używam go, ponieważ kod zwracany przez JSON może być zniekształcony, co w najlepszym wypadku może zrobić coś chwiejnego, w najgorszym razie ujawnić coś złego.


2
Myślę, że błędy w formatyzatorze JSON po stronie serwera z pewnością stanowią problem. Czy odpowiedź serwera zależy od dowolnego tekstu przesłanego przez użytkownika? Więc musisz uważać na XSS.
swilliams,

3
Jeśli twój serwer nie jest uwierzytelniany przez HTTPS, możesz ponieść atak typu man-in-the-middle, gdzie inny host przechwytuje żądanie i wysyła własne dane.
Ben Combee,

11
Jeśli ktoś może wykonać atak man-in-the-middle, może z łatwością wstrzyknąć wszystko do twoich skryptów.
el.pescado

10
Nie powinieneś w ogóle polegać na kodzie javascript ... Nie polegasz na niczym, co działa po stronie klienta ... Jeśli ktoś atakuje man-in-the-middle, dlaczego miałby zadzierać z twoimi obiektami json? Może on udostępnić ci inną stronę internetową i różne pliki js ...
Calmarius

5
Osobiście nie podoba mi się argument „zawsze są inne sposoby”. Można na przykład powiedzieć, że zawsze można uniknąć programowania obiektowego. To nie znaczy, że nie jest to świetna opcja. Jeśli rozumiesz eval i związane z nim niebezpieczeństwa, może to być świetne narzędzie do użycia we właściwych sytuacjach.
dallin

4

Widziałem, jak ludzie opowiadają się za tym, aby nie używać eval, ponieważ jest zły , ale widziałem, że ci sami ludzie używają Function i setTimeout dynamicznie, więc używają eval pod maskami : D

BTW, jeśli Twoja piaskownica nie jest wystarczająco pewna (na przykład, jeśli pracujesz na stronie, która umożliwia wstrzyknięcie kodu), eval jest ostatnim z twoich problemów. Podstawową zasadą bezpieczeństwa jest to, że wszystkie dane wejściowe są złe, ale w przypadku JavaScript nawet sam JavaScript może być zły, ponieważ w JavaScript możesz zastąpić dowolną funkcję i po prostu nie możesz być pewien, że używasz prawdziwej, więc, jeśli złośliwy kod uruchomi się przed tobą, nie możesz ufać żadnej wbudowanej funkcji JavaScript: D

Teraz epilog do tego postu brzmi:

Jeśli NAPRAWDĘ tego potrzebujesz (80% ewaluacji czasu NIE jest potrzebne) i jesteś pewien, co robisz, po prostu użyj eval (lub lepszej funkcji;)), zamknięcia i OOP pokrywają 80/90% w przypadku, gdy eval można zastąpić innym rodzajem logiki, reszta to kod generowany dynamicznie (na przykład, jeśli piszesz interpreter) i jak już powiedziałeś oceniając JSON (tutaj możesz użyć bezpiecznej oceny Crockford;))


I jak zauważył sam Crockford , obecne przeglądarki internetowe mają wbudowaną funkcję JSON.parse .
Ruud Helderman

4

Eval jest komplementarny do kompilacji używanej do tworzenia szablonów kodu. Przez szablonowanie mam na myśli, że piszesz uproszczony generator szablonów, który generuje użyteczny kod szablonu, który zwiększa szybkość programowania.

Napisałem framework, w którym programiści nie używają EVAL, ale używają naszego frameworka, który z kolei musi używać EVAL do generowania szablonów.

Wydajność EVAL można zwiększyć, stosując następującą metodę; zamiast wykonywać skrypt, musisz zwrócić funkcję.

var a = eval("3 + 5");

Powinien być zorganizowany jako

var f = eval("(function(a,b) { return a + b; })");

var a = f(3,5);

Buforowanie f z pewnością poprawi prędkość.

Również Chrome pozwala bardzo łatwo debugować takie funkcje.

Jeśli chodzi o bezpieczeństwo, użycie eval lub nie ma większego znaczenia,

  1. Przede wszystkim przeglądarka wywołuje cały skrypt w piaskownicy.
  2. Każdy kod, który jest zły w EVAL, jest zły w samej przeglądarce. Osoba atakująca lub ktokolwiek może łatwo wstrzyknąć węzeł skryptu do DOM i zrobić wszystko, jeśli będzie w stanie coś sprawdzić. Nie użycie EVAL nie zrobi żadnej różnicy.
  3. Szkodliwe są głównie słabe zabezpieczenia po stronie serwera. Słaba walidacja plików cookie lub słaba implementacja ACL na serwerze powoduje większość ataków.
  4. Niedawna luka w Javie itp. Występowała w natywnym kodzie Java. JavaScript był i jest przeznaczony do działania w piaskownicy, podczas gdy aplety zostały zaprojektowane do działania poza piaskownicą z certyfikatami itp., Które prowadzą do luk w zabezpieczeniach i wielu innych rzeczy.
  5. Pisanie kodu do naśladowania przeglądarki nie jest trudne. Wszystko, co musisz zrobić, to wysłać żądanie HTTP do serwera za pomocą ulubionego ciągu agenta użytkownika. Wszystkie narzędzia testujące i tak wyśmiewają przeglądarki; jeśli atakujący chce cię skrzywdzić, EVAL jest ostatecznością. Mają wiele innych sposobów radzenia sobie z bezpieczeństwem po stronie serwera.
  6. DOM przeglądarki nie ma dostępu do plików ani nazwy użytkownika. W rzeczywistości nic na maszynie, do którego eval nie może uzyskać dostępu.

Jeśli twoje zabezpieczenia po stronie serwera są wystarczająco solidne, aby ktokolwiek mógł zaatakować z dowolnego miejsca, nie powinieneś się martwić EVAL. Jak już wspomniałem, gdyby EVAL nie istniał, osoby atakujące mają wiele narzędzi do włamania się na Twój serwer, niezależnie od możliwości EVAL twojej przeglądarki.

Eval nadaje się tylko do generowania niektórych szablonów do wykonywania złożonego przetwarzania ciągów w oparciu o coś, co nie jest wcześniej używane. Na przykład wolę

"FirstName + ' ' + LastName"

W przeciwieństwie do

"LastName + ' ' + FirstName"

Jako moja nazwa wyświetlana, która może pochodzić z bazy danych i która nie jest zakodowana na stałe.


Możesz użyć funkcji zamiast eval - function (first, last) { return last + ' ' + first }.
Konrad Borowski

Nazwy kolumn pochodzą z bazy danych.
Akash Kava,

3
Zagrożeniem evalsą głównie inni użytkownicy . Załóżmy, że masz stronę ustawień, która pozwala ustawić sposób, w jaki Twoje imię i nazwisko będzie wyglądało dla innych. Powiedzmy również, że nie myślałeś bardzo jasno, kiedy to napisałeś, więc twoje pole wyboru ma opcje takie jak <option value="LastName + ' ' + FirstName">Last First</option>. Otwieram narzędzia programistyczne, zmieniam valueopcję na alert('PWNED!'), wybieram zmienioną opcję i przesyłam formularz. Teraz, za każdym razem, gdy inna osoba może zobaczyć moją wyświetlaną nazwę, ten kod jest uruchamiany.
cHao

@ cHao, Ten, o którym mówisz, jest przykładem słabego bezpieczeństwa po stronie serwera, serwer nigdy nie powinien akceptować danych, które można wykonać jako kod w przeglądarce dowolnej osoby. Po raz kolejny nie rozumiesz pojęcia słabego bezpieczeństwa po stronie serwera.
Akash Kava,

1
Możesz narzekać na bezpieczeństwo po stronie serwera, jeśli chcesz, ale evalchodzi o wykonanie kodu, który nie jest częścią skryptu, który napisałeś. Jeśli nie potrzebujesz do tego mocy (a prawie nigdy tego nie robisz), unikanie evalpomaga uniknąć całej kategorii problemów. To dobrze, jeśli kod po stronie serwera jest mniej niż idealny.
cHao

4

Podczas debugowania w Chrome (v28.0.1500.72) stwierdziłem, że zmienne nie są powiązane z zamknięciami, jeśli nie są używane w zagnieżdżonej funkcji, która powoduje zamknięcie. To chyba optymalizacja silnika JavaScript.

ALE : gdy eval()jest używany w funkcji, która powoduje zamknięcie, WSZYSTKIE zmienne funkcji zewnętrznych są powiązane z zamknięciem, nawet jeśli w ogóle nie są używane. Jeśli ktoś ma czas na sprawdzenie, czy może to spowodować wycieki pamięci, zostaw komentarz poniżej.

Oto mój kod testowy:

(function () {
    var eval = function (arg) {
    };

    function evalTest() {
        var used = "used";
        var unused = "not used";

        (function () {
            used.toString();   // Variable "unused" is visible in debugger
            eval("1");
        })();
    }

    evalTest();
})();

(function () {
    var eval = function (arg) {
    };

    function evalTest() {
        var used = "used";
        var unused = "not used";

        (function () {
            used.toString();   // Variable "unused" is NOT visible in debugger
            var noval = eval;
            noval("1");
        })();
    }

    evalTest();
})();

(function () {
    var noval = function (arg) {
    };

    function evalTest() {
        var used = "used";
        var unused = "not used";

        (function () {
            used.toString();    // Variable "unused" is NOT visible in debugger
            noval("1");
        })();
    }

    evalTest();
})();

Chciałbym tutaj podkreślić, że eval () niekoniecznie musi odnosić się do eval()funkcji natywnej . Wszystko zależy od nazwy funkcji . Kiedy więc wywołujesz rodzimego eval()z aliasem (powiedzmy, var noval = eval;a potem w funkcji wewnętrznej noval(expression);), wówczas ocena expressionmoże się nie powieść, gdy odnosi się do zmiennych, które powinny być częścią zamknięcia, ale tak naprawdę nie jest.



3

Dolna linia

Jeśli stworzyłeś lub zdezynfekowałeś kod eval, nigdy nie jest zły .

Nieco bardziej szczegółowe

evaljest złe, jeśli działa na serwerze przy użyciu danych wejściowych przesłanych przez klienta, który nie został utworzony przez programistę lub nie został oczyszczony przez programistę .

evalnie jest złe, jeśli działa na kliencie, nawet jeśli używasz niezaszyfrowanych danych wejściowych spreparowanych przez klienta .

Oczywiście należy zawsze dezynfekować dane wejściowe, aby mieć kontrolę nad zużyciem kodu.

Rozumowanie

Klient może uruchomić dowolny dowolny kod, nawet jeśli programista go nie kodował; Dotyczy to nie tylko tego, co ewaluowane, ale także wezwania do evalsiebie .


2

Jedynym przypadkiem, w którym powinieneś używać eval (), jest potrzeba dynamicznego uruchamiania JS w locie. Mówię o JS, który pobierasz asynchronicznie z serwera ...

... A 9 razy na 10 można łatwo tego uniknąć przez refaktoryzację.


Obecnie istnieją inne (i lepsze) sposoby asynchronicznego ładowania JavaScript z serwera: w3bits.com/async-javascript
Ruud Helderman

1

evalrzadko jest właściwym wyborem. Chociaż może istnieć wiele przypadków, w których możesz osiągnąć to, co musisz osiągnąć, łącząc skrypt razem i uruchamiając go w locie, zazwyczaj masz do dyspozycji znacznie potężniejsze i łatwiejsze w utrzymaniu techniki: notacja tablic asocjacyjnych ( obj["prop"]jest taka sama jak obj.prop) , zamknięcia, techniki obiektowe, techniki funkcjonalne - używaj ich zamiast tego.


1

Jeśli chodzi o skrypt klienta, myślę, że kwestia bezpieczeństwa jest kwestią sporną. Wszystko ładowane do przeglądarki podlega manipulacji i powinno być traktowane jako takie. Korzystanie z instrukcji eval () jest zerowe, gdy istnieją znacznie łatwiejsze sposoby wykonywania kodu JavaScript i / lub manipulowania obiektami w DOM, takimi jak pasek adresu URL w przeglądarce.

javascript:alert("hello");

Jeśli ktoś chce zmanipulować swój DOM, mówię: odejdź. Bezpieczeństwo, aby zapobiec wszelkiego rodzaju atakom, zawsze powinno być obowiązkiem aplikacji serwera, kropka.

Z pragmatycznego punktu widzenia nie ma korzyści z używania eval () w sytuacji, w której można zrobić inaczej. Istnieją jednak szczególne przypadki, w których NALEŻY zastosować ewaluację. Gdy tak, zdecydowanie można to zrobić bez ryzyka wysadzenia strony w powietrze.

<html>
    <body>
        <textarea id="output"></textarea><br/>
        <input type="text" id="input" />
        <button id="button" onclick="execute()">eval</button>

        <script type="text/javascript">
            var execute = function(){
                var inputEl = document.getElementById('input');
                var toEval = inputEl.value;
                var outputEl = document.getElementById('output');
                var output = "";

                try {
                    output = eval(toEval);
                }
                catch(err){
                    for(var key in err){
                        output += key + ": " + err[key] + "\r\n";
                    }
                }
                outputEl.value = output;
            }
        </script>
    <body>
</html>

6
Re „Korzystanie z instrukcji eval () jest zerowe, gdy istnieją znacznie łatwiejsze sposoby wykonywania javascript i / lub manipulowania obiektami w DOM”. Wstrzykiwanie kodu jest problemem, gdy jeden użytkownik może wprowadzić kod, który jest następnie uruchamiany w przeglądarce innego użytkownika. Konsole przeglądarki same w sobie nie pozwalają jednemu użytkownikowi uruchamiać kodu w przeglądarce innego użytkownika, więc nie mają znaczenia przy podejmowaniu decyzji, czy warto zabezpieczyć się przed wstrzyknięciem kodu.
Mike Samuel

Nie jest <head></head>wymagane, nawet jeśli jest puste?
Peter Mortensen

2
Ta odpowiedź całkowicie ignoruje ryzyko związane z XSS .
Ruud Helderman

1

Po stronie serwera eval jest przydatny w przypadku zewnętrznych skryptów, takich jak sql lub influxdb lub mongo. Tam, gdzie można dokonać niestandardowej weryfikacji w czasie wykonywania bez ponownego wdrażania usług.

Na przykład usługa osiągnięć z następującymi metadanymi

{
  "568ff113-abcd-f123-84c5-871fe2007cf0": {
    "msg_enum": "quest/registration",
    "timely": "all_times",
    "scope": [
      "quest/daily-active"
    ],
    "query": "`SELECT COUNT(point) AS valid from \"${userId}/dump/quest/daily-active\" LIMIT 1`",
    "validator": "valid > 0",
    "reward_external": "ewallet",
    "reward_external_payload": "`{\"token\": \"${token}\", \"userId\": \"${userId}\", \"amountIn\": 1, \"conversionType\": \"quest/registration:silver\", \"exchangeProvider\":\"provider/achievement\",\"exchangeType\":\"payment/quest/registration\"}`"
  },
  "efdfb506-1234-abcd-9d4a-7d624c564332": {
    "msg_enum": "quest/daily-active",
    "timely": "daily",
    "scope": [
      "quest/daily-active"
    ],
    "query": "`SELECT COUNT(point) AS valid from \"${userId}/dump/quest/daily-active\" WHERE time >= '${today}' ${ENV.DAILY_OFFSET} LIMIT 1`",
    "validator": "valid > 0",
    "reward_external": "ewallet",
    "reward_external_payload": "`{\"token\": \"${token}\", \"userId\": \"${userId}\", \"amountIn\": 1, \"conversionType\": \"quest/daily-active:silver\", \"exchangeProvider\":\"provider/achievement\",\"exchangeType\":\"payment/quest/daily-active\"}`"
  }
}

Które następnie pozwalają

  • Bezpośrednie wstrzykiwanie obiektów / wartości przez dosłowny ciąg znaków w jsonie, przydatne do tworzenia szablonów tekstów

  • Może być używany jako komparator, powiedzmy, że ustalamy zasady sprawdzania poprawności zadań lub wydarzeń w CMS

Przeciw tego:

  • Mogą występować błędy w kodzie i powodować awarie w serwisie, jeśli nie zostaną w pełni przetestowane.

  • Jeśli haker potrafi pisać skrypty w twoim systemie, to znaczy, że nie rozumiesz.

  • Jednym ze sposobów sprawdzania poprawności skryptu jest przechowywanie skrótów skryptów w bezpiecznym miejscu, aby można je było sprawdzić przed uruchomieniem.


Miły. Kiedy zadałem pytanie, nawet nie myślałem o JS po stronie serwera.
Richard Turner

1

Myślę, że wszelkie przypadki uzasadnienia ewaluacji byłyby rzadkie. Jesteś bardziej prawdopodobne, aby używać go myśląc, że jest to uzasadnione niż jesteś z niego korzystać, gdy jest rzeczywiście uzasadnione.

Kwestie bezpieczeństwa są najbardziej znane. Pamiętaj jednak, że JavaScript korzysta z kompilacji JIT i działa to bardzo słabo z eval. Eval przypomina kompilator w pewnym sensie, a JavaScript musi być w stanie przewidzieć kod z wyprzedzeniem (do pewnego stopnia) w celu bezpiecznego i prawidłowego zastosowania optymalizacji wydajności i określania zakresu. W niektórych przypadkach wpływ na wydajność może nawet wpłynąć na inny kod poza eval.

Jeśli chcesz dowiedzieć się więcej: https://github.com/getify/You-Dont-Know-JS/blob/master/scope%20%26%20closures/ch2.md#eval


0

Można z niego korzystać, jeśli masz pełną kontrolę nad kodem przekazywanym do evalfunkcji.


2
Jeśli masz pełną kontrolę nad tym, co przekazujesz eval, wtedy pojawia się duże pytanie, kiedy ma to sens, że jest to ciąg znaków zamiast prawdziwego JS?
cHao

@cHao Na przykład, jeśli masz dużą aplikację do gier (5-10 MB Javascript), lepiej najpierw zbudować prosty, szybko ładujący AJAX-Preloader (1kb), który ładuje duży skrypt główny, wyświetlając jednocześnie Ładowanie Bar lub coś podobnego. Po pobraniu możesz użyć „eval (źródło)” lub lepszej „nowej funkcji (źródło)”, aby uruchomić załadowany skrypt aplikacji. W ten sposób użytkownik może wizualnie zobaczyć, że aplikacja potrzebuje czasu na pobranie do momentu uruchomienia gry. Bez tego użytkownik musi czekać na załadowanie całej aplikacji bez wizualnej informacji zwrotnej.
SammieFox,

@SammieFox Istnieją inne (i lepsze) sposoby na zrobienie tego, w szczególności <script async="true" src="...">. Zobacz także: w3bits.com/async-javascript
Ruud Helderman

Odpowiedź to niebezpieczna rada; zbyt wielu programistów ma fałszywe poczucie kontroli. Ta rada ma sens w przypadku oprogramowania, które nie jest już aktywnie utrzymywane. Ale takie oprogramowanie należy uznać za martwe.
Ruud Helderman

0

Tylko podczas testów, jeśli to możliwe. Zauważ też, że eval () działa znacznie wolniej niż inne wyspecjalizowane ewaluatory JSON itp.


0

Nie ma powodu, aby nie używać eval (), o ile masz pewność, że źródło kodu pochodzi od ciebie lub faktycznego użytkownika. Nawet jeśli może on manipulować tym, co zostanie wysłane do funkcji eval (), nie stanowi to problemu bezpieczeństwa, ponieważ jest on w stanie manipulować kodem źródłowym strony internetowej i dlatego może zmienić sam kod JavaScript.

Więc ... kiedy nie używać eval ()? Eval () nie powinien być używany tylko wtedy, gdy istnieje szansa, że ​​strona trzecia może go zmienić. Jak przechwytywanie połączenia między klientem a serwerem (ale jeśli jest to problem, użyj HTTPS). Nie powinieneś eval () do parsowania kodu napisanego przez innych jak na forum.


Re „Nie ma powodu, aby nie używać eval (), o ile masz pewność, że źródło kodu pochodzi od ciebie lub faktycznego użytkownika”. Zakłada się, że istnieje jeden użytkownik. Przesłanka ta nie jest określona w PO. Gdy jest wielu użytkowników, nieostrożność evalciągu złożonego z treści jednego użytkownika może pozwolić temu użytkownikowi na wykonanie kodu w przeglądarce drugiego użytkownika.
Mike Samuel,

@MikeSamuel, eval może wykonać kod w przeglądarce innego użytkownika, nie słyszałem o tym, czy próbowałeś tego? To się nigdy nie zdarzyło w historii przeglądania, czy możesz nam pokazać przykład?
Akash Kava,

@AkashKava, ciąg może pochodzić od jednego klienta użytkownika, może być przechowywany w bazie danych, a następnie obsługiwany w innej przeglądarce, która evaljest nim. To się zdarza cały czas.
Mike Samuel,

@MikeSamuel baza danych? gdzie? kto podaje zły ciąg? czy nie jest to wina po stronie serwera? po pierwsze EVAL nie można winić za źle napisany kod po stronie serwera. Użyj jsfiddle i pokaż światu prawdziwy przykład, w którym może wyrządzić szkodę.
Akash Kava,

2
@AkashKava, nie rozumiem twojego pytania. Nie mówimy o konkretnej aplikacji, ale powody, by jej nie używać eval. Jak warto obwiniać serwer? Jeśli ktoś powinien być winny, powinien to być napastnik. Niezależnie od winy klient, który nie jest podatny na XSS pomimo błędów na serwerze, jest lepszy niż klient, który jest podatny na atak, a wszystkie pozostałe są jednakowe.
Mike Samuel,

0

Jeśli jest to naprawdę potrzebne, eval nie jest złe. Ale 99,9% zastosowań eval, na które się natknęłam, nie jest potrzebnych (nie wliczając rzeczy setTimeout).

Dla mnie zło nie jest sprawą ani nawet kwestią bezpieczeństwa (cóż, pośrednio jest to jedno i drugie). Wszystkie takie niepotrzebne zastosowania eval przyczyniają się do utrzymania piekła. Narzędzia do refaktoryzacji są wyrzucane. Poszukiwanie kodu jest trudne. Nieoczekiwanymi skutkami tych ewolucji są legion.


5
eval nie jest konieczne dla setTimeout. Można tam również użyć odwołania do funkcji.
Matthew Crumley,

0

Kiedy JavaScript eval () nie jest zły?

Zawsze staram się zniechęcać do używania eval . Niemal zawsze dostępne jest bardziej czyste i łatwe w utrzymaniu rozwiązanie. Eval nie jest potrzebny nawet do parsowania JSON . Eval dodaje do piekła utrzymania . Nie bez powodu czują się rozczarowani takimi mistrzami jak Douglas Crockford.

Ale znalazłem jeden przykład, w którym należy go użyć:

Kiedy musisz przekazać wyrażenie.

Na przykład mam funkcję, która konstruuje google.maps.ImageMapTypedla mnie ogólny obiekt, ale muszę powiedzieć mu przepis, w jaki sposób powinien skonstruować adres URL kafelka z parametrów zoomi coord:

my_func({
    name: "OSM",
    tileURLexpr: '"http://tile.openstreetmap.org/"+b+"/"+a.x+"/"+a.y+".png"',
    ...
});

function my_func(opts)
{
    return new google.maps.ImageMapType({
        getTileUrl: function (coord, zoom) {
            var b = zoom;
            var a = coord;
            return eval(opts.tileURLexpr);
        },
        ....
    });
}

3
Wygląda na to, że można to zmienić tak, że eval () nie jest konieczne - tileURLexpr jest tylko szablonem, więc pewne rozsądne użycie metody replace () wykona zadanie. Przypomina mi to jednak przykład, który miałem na myśli, gdy zadałem pytanie, które dotyczyło umożliwienia użytkownikowi określenia wzoru matematycznego do oceny, podobnego do funkcjonalności arkusza kalkulacyjnego. Oczywiście nie wspominałem o tym wtedy, ponieważ nie chciałem wpływać na odpowiedzi!
Richard Turner

8
tileURL: function (zoom, coord) { return 'http://tile.openstreetmap.org/' + b + '/' + a.x + '/' + a.y + '.png'; },
Casey Chu,

0

Mój przykład użycia eval: import .

Jak to zwykle się robi.

var components = require('components');
var Button = components.Button;
var ComboBox = components.ComboBox;
var CheckBox = components.CheckBox;
...
// That quickly gets very boring

Ale dzięki pomocy evali małej funkcji pomocnika uzyskuje się znacznie lepszy wygląd:

var components = require('components');
eval(importable('components', 'Button', 'ComboBox', 'CheckBox', ...));

importable może wyglądać (ta wersja nie obsługuje importowania konkretnych elementów).

function importable(path) {
    var name;
    var pkg = eval(path);
    var result = '\n';

    for (name in pkg) {
        result += 'if (name !== undefined) throw "import error: name already exists";\n'.replace(/name/g, name);
    }

    for (name in pkg) {
        result += 'var name = path.name;\n'.replace(/name/g, name).replace('path', path);
    }
    return result;
}

2
+1 dla idei, ale masz problem tutaj: .replace(/name/g, name).replace('path', path). Jeśli namezawiera ciąg znaków, "path"możesz uzyskać niespodzianki.
wberry

1
Zadeklarowanie jednej zmiennej dla każdej właściwości componentsto możliwy zapach kodu; refaktoryzacja kodu może całkowicie wyeliminować „problem”. Twoje obecne rozwiązanie to tylko cukier syntaktyczny. Jeśli nalegasz na to, polecam napisanie własnego preprocesora, który zostanie wykonany przed wdrożeniem. To powinno trzymać evalsię z dala od kodu produkcyjnego.
Ruud Helderman

0

Eval nie jest zły, tylko niewłaściwie używany.

Jeśli stworzyłeś do niego kod lub możesz mu zaufać, jest w porządku. Ludzie wciąż mówią o tym, jak wkład użytkownika nie ma znaczenia przy eval. Cóż, jakby ~

Jeśli dane wejściowe od użytkownika trafiają na serwer, a następnie wracają do klienta, a kod jest używany w ewaluacji bez dezynfekcji. Gratulacje, otworzyłeś skrzynkę pandory, aby dane użytkownika mogły być wysyłane do kogokolwiek.

W zależności od tego, gdzie znajduje się eval, wiele witryn korzysta ze SPA, a eval może ułatwić użytkownikowi dostęp do wewnętrznych aplikacji, które w innym przypadku nie byłyby łatwe. Teraz mogą stworzyć fałszywe rozszerzenie przeglądarki, które może nagrać taśmę na tę ewaluację i ponownie ukraść dane.

Muszę tylko dowiedzieć się, jaki jest sens korzystania z eval. Generowanie kodu nie jest tak naprawdę idealne, gdy można po prostu stworzyć metody do wykonywania takich czynności, używania obiektów itp.

Dobry przykład użycia eval. Twój serwer odczytuje utworzony przez ciebie plik swagger. Wiele parametrów adresu URL jest utworzonych w tym formacie {myParam}. Więc chcesz przeczytać adresy URL, a następnie przekonwertować je na ciągi szablonów bez konieczności wykonywania skomplikowanych zamian, ponieważ masz wiele punktów końcowych. Więc możesz zrobić coś takiego. Zauważ, że to bardzo prosty przykład.

const params = { id: 5 };

const route = '/api/user/{id}';
route.replace(/{/g, '${params.');

// use eval(route); to do something

-1

Generowanie kodu Niedawno napisałem bibliotekę o nazwie Hyperbars, która wypełnia lukę między domem wirtualnym a kierownicą . Robi to, analizując szablon kierownicy i konwertując go do hiperskryptu . Indeks górny jest najpierw generowany jako ciąg, a przed zwróceniem eval()przekształca go w kod wykonywalny. eval()W tej szczególnej sytuacji znalazłem dokładne przeciwieństwo zła.

Zasadniczo od

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

Do tego

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

Wydajność eval()nie jest również problemem w takiej sytuacji, ponieważ wystarczy zinterpretować wygenerowany ciąg tylko raz, a następnie wielokrotnie użyć wyjściowego pliku wykonywalnego.

Można zobaczyć, w jaki sposób generowania kodu został osiągnięty, jeśli jesteś ciekawy tutaj .


„Hiperskrypt jest najpierw generowany jako ciąg znaków (...)” Bardziej sensowne jest generowanie całego kodu w fazie kompilacji, zapisanie wynikowego kodu hiperskryptu w osobnym pliku wykonywalnym (.js), a następnie wdrożenie tego pliku do przetestowania i produkcja. Podoba mi się sposób, w jaki używasz generowania kodu. To tylko evalwskazówka, że ​​część odpowiedzialności, która należy do czasu kompilacji, przeszła w środowisko wykonawcze.
Ruud Helderman

-1

Wierzę, że eval to bardzo potężna funkcja dla aplikacji internetowych po stronie klienta i bezpieczne ... Tak bezpieczne jak JavaScript, które nie są. :-) Problemy z bezpieczeństwem są zasadniczo problemem po stronie serwera, ponieważ teraz za pomocą narzędzia takiego jak Firebug możesz atakować dowolną aplikację JavaScript.


1
Korzystanie z evalzabezpieczeń musi być zabezpieczone przed atakami XSS, co nie zawsze jest łatwe.
Benjamin,

-1

Eval jest przydatny do generowania kodu, gdy nie masz makr.

Na przykład (głupi) przykład, jeśli piszesz kompilator Brainfuck , prawdopodobnie będziesz chciał zbudować funkcję, która wykonuje sekwencję instrukcji jako ciąg znaków, i wypróbować ją, aby zwrócić funkcję.


Albo piszesz kompilator (który zapisuje, a nie generuje generowany kod) lub piszesz interpreter (gdzie każda instrukcja ma wstępnie skompilowaną implementację). Nie jest to również przypadek użycia eval.
Ruud Helderman

Jeśli wygenerowałeś kod javascript i chciałbyś go natychmiast wykonać (powiedzmy, że chodzi o zwiększenie wydajności w porównaniu z bezpośrednią interpretacją), byłby to przypadek użycia eval.
Erik Haliewicz

Słuszna uwaga; Widziałem przykład w tym artykule o Blockly . Jestem zaskoczony, że Google zaleca eval, gdy alternatywa ( funkcja ) jest zarówno szybsza ( jak wyjaśniono w MDN ), jak i bardziej niezawodna (zapobiega nieprzewidywalnym błędom dzięki lepszej izolacji między wygenerowanym kodem a innym „pomocniczym” kodem na tej samej stronie).
Ruud Helderman

-5

Podczas analizowania struktury JSON za pomocą funkcji parsowania (na przykład jQuery.parseJSON), oczekuje ona doskonałej struktury pliku JSON (każda nazwa właściwości jest w cudzysłowie). JavaScript jest jednak bardziej elastyczny. Dlatego możesz użyć eval (), aby tego uniknąć.


4
Nie używaj na ślepo eval, szczególnie. podczas pobierania danych JSON ze źródła zewnętrznego. Zobacz JSON.Stringify bez cudzysłowów na właściwości? dla poprawnego podejścia do parsowania „JSON bez cytowanych nazw kluczy”.
Rob W

2
Jeśli nie używa podwójnych cudzysłowów wokół nazw właściwości, może to być ciąg znaków reprezentujący dosłowność obiektu, ale nie jest to JSON . JSON definiuje nazwy właściwości jako a stringi definiuje stringjako sekwencję zerową lub więcej znaków Unicode, owiniętych podwójnymi cudzysłowami, za pomocą znaków odwrotnego ukośnika.
Bezużyteczny kod

Zobacz artykuł Nikolasa Zakasa - „eval () nie jest zły, po prostu źle zrozumiany” nczonline.net/blog/2013/06/25/eval-isnt-evil-just-misunderstood
vitmalina

@vitmalina Z artykułu Zakasa: „Może to być niebezpieczne, jeśli pobierasz dane wejściowe od użytkownika i uruchamiasz je za pomocą eval (). Jeśli jednak dane wejściowe nie pochodzą od użytkownika, czy istnieje jakieś realne niebezpieczeństwo?” To jest właśnie problem. Gdy Twój kod wykracza poza proporcje „hello world”, szybko staje się niemożliwe udowodnienie, że nie wyciekasz przez użytkownika eval. Jest to niedopuszczalne w żadnej poważnej aplikacji sieci Web z wieloma dzierżawcami, z dziesiątkami programistów pracujących na tej samej podstawie kodu.
Ruud Helderman
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.