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ć?
javascript
coding-style
eval
Richard Turner
źródło
źródło
Odpowiedzi:
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.
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.
źródło
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.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.źródło
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 ().
źródło
eval
nie będzie również poprawnie analizować wszystkich prawidłowych ciągów JSON. Na przykładJSON.parse(' "\u2028" ') === "\u2028"
, aleeval(' "\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.Zdobądźmy prawdziwych ludzi:
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?
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.
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).
źródło
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ładsetTimeout()
pozwala przekazać funkcję zamiast ewaluować.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.
źródło
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;))
źródło
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ę.
Powinien być zorganizowany jako
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,
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ę
W przeciwieństwie do
Jako moja nazwa wyświetlana, która może pochodzić z bazy danych i która nie jest zakodowana na stałe.
źródło
function (first, last) { return last + ' ' + first }
.eval
są 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, zmieniamvalue
opcję naalert('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.eval
chodzi 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), unikanieeval
pomaga uniknąć całej kategorii problemów. To dobrze, jeśli kod po stronie serwera jest mniej niż idealny.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:
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 rodzimegoeval()
z aliasem (powiedzmy,var noval = eval;
a potem w funkcji wewnętrznejnoval(expression);
), wówczas ocenaexpression
może się nie powieść, gdy odnosi się do zmiennych, które powinny być częścią zamknięcia, ale tak naprawdę nie jest.źródło
Microsoft wyjaśnia, dlaczego eval () działa wolno w przeglądarce na blogu IE , IE + JavaScript Zalecenia dotyczące wydajności Część 2: Nieefektywność kodu JavaScript .
źródło
Dolna linia
Jeśli stworzyłeś lub zdezynfekowałeś kod
eval
, nigdy nie jest zły .Nieco bardziej szczegółowe
eval
jest 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ę .eval
nie 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
eval
siebie .źródło
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ę.
źródło
eval
rzadko 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 jakobj.prop
) , zamknięcia, techniki obiektowe, techniki funkcjonalne - używaj ich zamiast tego.źródło
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.
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.
źródło
<head></head>
wymagane, nawet jeśli jest puste?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
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.
źródło
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
źródło
Można z niego korzystać, jeśli masz pełną kontrolę nad kodem przekazywanym do
eval
funkcji.źródło
eval
, wtedy pojawia się duże pytanie, kiedy ma to sens, że jest to ciąg znaków zamiast prawdziwego JS?<script async="true" src="...">
. Zobacz także: w3bits.com/async-javascriptTylko podczas testów, jeśli to możliwe. Zauważ też, że eval () działa znacznie wolniej niż inne wyspecjalizowane ewaluatory JSON itp.
źródło
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.
źródło
eval
cią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.eval
jest nim. To się zdarza cały czas.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.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.
źródło
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.ImageMapType
dla mnie ogólny obiekt, ale muszę powiedzieć mu przepis, w jaki sposób powinien skonstruować adres URL kafelka z parametrówzoom
icoord
:źródło
tileURL: function (zoom, coord) { return 'http://tile.openstreetmap.org/' + b + '/' + a.x + '/' + a.y + '.png'; },
Mój przykład użycia
eval
: import .Jak to zwykle się robi.
Ale dzięki pomocy
eval
i małej funkcji pomocnika uzyskuje się znacznie lepszy wygląd:importable
może wyglądać (ta wersja nie obsługuje importowania konkretnych elementów).źródło
.replace(/name/g, name).replace('path', path)
. Jeśliname
zawiera ciąg znaków,"path"
możesz uzyskać niespodzianki.components
to 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ćeval
się z dala od kodu produkcyjnego.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.źródło
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
Do tego
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 .
źródło
eval
wskazówka, że część odpowiedzialności, która należy do czasu kompilacji, przeszła w środowisko wykonawcze.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.
źródło
eval
zabezpieczeń musi być zabezpieczone przed atakami XSS, co nie zawsze jest łatwe.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ę.
źródło
eval
.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).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ąć.
źródło
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”.string
i definiujestring
jako sekwencję zerową lub więcej znaków Unicode, owiniętych podwójnymi cudzysłowami, za pomocą znaków odwrotnego ukośnika.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.