Co stanowi „niewłaściwe użycie” funkcji javascript Eval? [Zamknięte]

13

Eval jest niezwykle kontrowersyjną funkcją językową. Douglas Crockford całkowicie odrzuca to. Zastanawiam się, jakie konkretne ryzyko powoduje Eval. Zgodnie z tym pytaniem Improper use of eval opens up your code for injection attacks.

Jakie są niewłaściwe zastosowania polecenia Eval i jakie luki w zabezpieczeniach otwierają?

Derek Adair
źródło

Odpowiedzi:

31

Kiedy wdrażałem silnik JScript, zalecałem wydrukowanie koszulki EVAL IS EVIL , ale niestety nigdy się do tego nie zabraliśmy.

Moim największym problemem z eval nie jest oczywisty atak polegający na wstrzykiwaniu złośliwego kodu, choć z pewnością budzi to ogromne zaniepokojenie. Moim największym problemem jest to, że ludzie używają go jako naprawdę dużego młota do rozwiązywania naprawdę małych problemów. Większość rzeczywistych zastosowań, które widziałem na „ewaluacji” na wolności, kiedy pracowałem w zespole JScript, można było w prosty sposób rozwiązać przy użyciu tabeli odnośników; a ponieważ każdy obiekt w JScript jest już tabelą odnośników , nie jest to uciążliwe. Eval ponownie uruchamia kompilator i całkowicie niweczy zdolność kompilatora do optymalizacji kodu .

Aby uzyskać więcej przemyśleń w tym duchu, zobacz moje artykuły z 2003 roku na ten temat:

Ogólne zło:

http://blogs.msdn.com/b/ericlippert/archive/2003/11/01/53329.aspx

Szatańskie ataki wtryskowe:

http://blogs.msdn.com/b/ericlippert/archive/2003/11/04/53335.aspx

Eric Lippert
źródło
Jakie są twoje przemyślenia na temat evaltworzenia dużych fragmentów kodu tak, jak lubią aplikacje takie jak JSPacker? JSPacker + gzip zwykle skutkuje mniejszymi rozmiarami plików niż którekolwiek z rozwiązań, ale jak słusznie zauważyłeś, zasadniczo uruchamia kompilator dwa razy na tym samym fragmencie kodu, a także narzuca pewne koszty zastępowania łańcucha.
Matthew Scharley
2
@Matthew: Często dochodzi do kompromisu między przestrzenią a czasem, a skorzystanie z tego kompromisu może czasem być dużą wygraną. Jeśli celem tej techniki jest poprawa wydajności, to uważam, że jeśli staranne pomiary wykażą, że jest to znacząca wygrana w realistycznych scenariuszach bez wprowadzania luk bezpieczeństwa, świetnie, zrób to. Ale nie wiem wystarczająco dużo o konkretnej technice, aby skrytykować jej szczegóły.
Eric Lippert
Życzę jedną z odpowiedzi, które widziałem albo tutaj albo na więc sama stwierdziły co swoje drugie stany Link: że eval()to nie zagrożenie bezpieczeństwa na klienta (przeglądarki) kodu.
sq33G
4

Większość dziur w zabezpieczeniach to te same dziury, co w przypadku wstrzykiwania SQL, a mianowicie łączenie danych wejściowych użytkownika z kodem JavaScript. Różnica polega na tym, że chociaż istnieją sposoby, aby upewnić się, że tak się nie stanie z SQL, niewiele można zrobić z JavaScript.

Jako trywialny i bezużyteczny przykład uproszczony kalkulator JavaScript:

textbox1.value = eval(textbox2.value);

Jednym z poprawnych przykładów użycia są niektóre programy pakujące JavaScript, które kompresują JavaScript, wyciągając typowe słowa i zastępując je krótkimi 1-2 znakowymi zamiennikami. Pakowacz wyprowadza to wszystko wraz z kodem zastępującym ciąg znaków na podstawie wygenerowanego słownika, a następnie sprawdza wynik.

Matthew Scharley
źródło
1

Są pewne rzeczy, które są niemożliwe do zrobienia w JS bez funkcji eval (podobny eval, Functioni być może więcej).

Weźmy applyna przykład. Jest łatwy w użyciu, gdy używasz go do zwykłych wywołań funkcji:

foo.apply(null, [a, b, c])

Ale jak byś to zrobił dla obiektów tworzonych za pomocą nowej składni?

new Foo.apply(null, [a, b, c]) nie działa, ani nie robi podobnych form.

Ale możesz obejść to ograniczenie za pomocą evallub Function(używam Functionw tym przykładzie):

Function.prototype.New = (function () {
    var fs = [];
    return function () {
        var f = fs[arguments.length];
        if (f) {
            return f.apply(this, arguments);
        }
        var argStrs = [];
        for (var i = 0; i < arguments.length; ++i) {
            argStrs.push("a[" + i + "]");
        }
        f = new Function("var a=arguments;return new this(" + argStrs.join() + ");");
        if (arguments.length < 100) {
            fs[arguments.length] = f;
        }
        return f.apply(this, arguments);
    };
}) ();

Przykład:

Foo.New.apply(null, [a, b, c]);

Oczywiście można ręcznie budować używane funkcje Function.prototype.New, ale nie tylko jest to pełne i niezgrabne, ale (z definicji) musi być skończone. Functionumożliwia działanie kodu dla dowolnej liczby argumentów.

Thomas Eding
źródło
2
To prawda, ale pytanie OP brzmiało: „ Jakie są niewłaściwe zastosowania polecenia Eval i jakie luki w zabezpieczeniach otwierają? ”, A nie „ Jaki jest dobry użytek eval()?
Ross Patterson
1
@RossPatterson: Chyba głównie patrzyłem na tytuł pytania haha.
Thomas Eding,
Mimo to +1 za znalezienie dobrego wykorzystania złej funkcji językowej :-)
Ross Patterson
1

Nieco bezpośrednią odpowiedzią z mojej strony było opracowanie obszaru testowania API zorientowanego na programistów. Daliśmy obszar tekstowy na stronie i przycisk Uruchom. Centralny punkt strony polegał na tym, że mogli wypróbować rzeczy w JavaScript w stosunku do naszego interfejsu API komunikującego się z iframe, co nie było tak łatwe w lokalnym środowisku.

Cokolwiek złośliwego, co można było zrobić w tym przypadku, mogło być również zrobione przez programistę otwierającego swoje narzędzia F12.

Katana314
źródło
0

Zgadzam się, że należy go bardzo rzadko używać, ale znalazłem potężny przypadek użycia eval.

W Firefoksie jest eksperymentalna nowa funkcja o nazwie asm.js, nad którą pracuję. Pozwala to na skompilowanie ograniczonego podzbioru języka JavaScript w natywny kod. Tak, to jest bardzo niesamowite, ale ma ograniczenia. Możesz myśleć o ograniczonym podzbiorze Javascript jako języku podobnym do C osadzonym w Javascript. Tak naprawdę nie jest przeznaczony do czytania ani pisania przez ludzi.

Ten ograniczony podzbiór Javascript nie pozwala mi wstrzykiwać mojego kodu wygenerowanego w środowisku wykonawczym do skompilowanego kodu po jego skompilowaniu.

Napisałem jakiś kod, który pozwala użytkownikowi na zapisanie, w znanym zapisie, wyrażenia matematycznego i konwersję w locie do kodu asm.js. O ile nie chcę, aby kod był przetwarzany po stronie serwera (czego nie robię), evaljest to jedyne narzędzie, które pozwala mi przetwarzać wynikowy kod przez przeglądarkę w czasie rzeczywistym.

Ciastka Z Mąki Ryżowej
źródło
0

Jak zauważyli inni, najważniejszą rzeczą podczas pracy evaljest zapewnienie bezpieczeństwa. W tym celu chcesz dokładnie sprawdzić argumenty i zachować evalprosty kod, ponieważ generalnie trudniej jest utrzymać i zabezpieczyć kod wygenerowany w czasie wykonywania.

Biorąc to pod uwagę, lubię korzystać evalz dwóch rodzajów rzeczy (nawet jeśli prawdopodobnie byłyby lepsze, mniej szatańskie alternatywy):

  1. Ponieważ evaljest to sposób „jawnego buforowania kodu”, można go wykorzystać do poprawy wydajności. Pamiętaj, że optymalizatory są zawsze ulepszane, ale po prostu nie ma gwarancji, co mogą dla Ciebie zrobić. Wyrażając rzeczy w kodzie, faktycznie możesz pomóc optymalizatorowi w podejmowaniu mądrzejszych decyzji.
  2. Można go również wykorzystać do zapewnienia podstawowych form bezpieczeństwa typu, a także innych funkcji językowych, których brakuje JS, bez pogorszenia wydajności.

Na przykład to wstępnie skompilowane podejście do iteratora obiektów wykazuje wyraźne korzyści w zakresie wydajności podczas evaliteracji właściwości obiektu. Pokazuje także początki potężnego systemu typów, który może zapewnić ukryte sprawdzanie ograniczeń i wiele więcej, za niewielką opłatą.

Wielu doświadczonych programistów Javascript prawdopodobnie zwróciłoby uwagę, że jest to wielka dziura, ponieważ jeśli zaczniesz pisać JavaScript w ten sposób, zasadniczo zmienisz sposób korzystania z języka. To jednak niekoniecznie musi być złą rzeczą dla tych, którzy lubią Javascript, ale brakuje im także podstawowych funkcji językowych, które można osiągnąć jedynie poprzez zmianę sposobu, w jaki korzystamy z samego języka.

Domi
źródło
-3

Mam sytuację, w której eval wygląda jak droga, oceniając ciąg i zwracając istniejącą zmienną, której nazwa jest taka sama jak ciąg.

Mam kilka tabel: table1ResultsTable, table2ResultsTable, tableNResultsTable. Mam zmienne skonfigurowane o tych samych nazwach, które są obiektami danych jQuery. Używam ich do konfigurowania tabel i wywoływania na nich funkcji danych jQuery.

Każda tabela ma przypisaną klasę = „resultsTable”. Teraz muszę uzyskać zmienną po kliknięciu tabeli. Robię to:

$('resultsTable).on('click', 'td', function(event) {
    var cResultsTable = eval(event.target.parentElement.parentElement.parentElement.id);
    [etc]
});

Dostaję więc identyfikator tabeli, w której kliknięto komórkę, która ma taką samą nazwę jak odpowiadająca jej zmienna obiektu danych. Jeśli ktoś ma sugestię dotyczącą ulepszenia, chciałbym o tym wiedzieć.

BobRodes
źródło
1
Dlaczego potrzebujesz eval? Identyfikator powinien być prostym ciągiem, a nie kodem. W każdym razie event.target.parentElement.parentElement.parentElementjest przerażające. Ponadto spodziewam się, że wywołanie eval wygeneruje błąd „błąd referencyjny: [id] nie jest zdefiniowany”, chyba że celowo używasz identyfikowalnych identyfikatorów (który jest zepsuty, zły i może spowodować dziwne rzeczy, jeśli skończysz generowanie duplikatów identyfikatorów).
Brian
Być może nie wyrażam się jasno. Identyfikator JEST zwykłym ciągiem. To identyfikator klikniętej tabeli. Mam także zmienną obiektową (ustawioną metodą jQuery datatable (), odwołującą się do tej samej tabeli) o tej samej nazwie co identyfikator. Próbuję uzyskać tę zmienną, uzyskać dostęp do jej funkcji (w rzeczywistości, aby dodać klasę „row_selected” do wybranego wiersza), po kliknięciu tabeli. Odkąd to napisałem, wprowadziłem jednak ulepszenie. Po prostu umieszczam wszystkie odwołania do obiektu w tablicy, nazywam elementy tak samo jak id i podłączam do niego ciąg id, aby uzyskać referencję do obiektu.
BobRodes,
Brian, jeśli masz lepszy sposób na znalezienie identyfikatora klikniętego stołu, jestem cały w uszach. W przypadku funkcji datatables muszę obsłużyć zdarzenie click względem komórki i dodać klasę row_selected do jej parentNode. Dlaczego nie mogę po prostu obsłużyć zdarzenia wiersza i dodać do niego bezpośrednio klasy, nie wiem, ale wiersz nie pokazuje się jako wybrany, gdy to robię.
BobRodes,
1
Być może nie wyrażam się jasno. Jeśli wywołasz eval na zwykłym łańcuchu (tj. Innym niż JS), zgłaszasz wyjątek. Zatem użycie eval nie ma sensu. Jeśli zdarzy się, że generujesz zmienną obiektową o tej samej nazwie co identyfikator, twój kod będzie działał ... ale wydaje mi się, że jest uszkodzony. Wolę utworzyć zmienną za pomocą document.getElementById(ID).MySpecialProperty = MYSPECIALPROPERTYVALUE(nie wspominając, że to też świetne, ale jest lepsze niż eval). Jak zauważa Eric Lippert, „każdy obiekt w JScript jest już tabelą odnośników”.
Brian
Ok, więc mówisz, aby dodać właściwość do odwołania do elementu i ustawić ją na odwołanie do obiektu danych? To również brzmi dla mnie mocniej.
BobRodes