Когда eval () JavaScript не является злом?

264

Я пишу некоторый код JavaScript для анализа введенных пользователем функций (для работы с таблицами). Разобрав формулу, я смог преобразовать ее в JavaScript и запустить eval()на ней, чтобы получить результат.

Тем не менее, я всегда уклонялся от использования, eval()если я могу избежать этого, потому что это зло (и, правильно или неправильно, я всегда думал, что это еще более зло в JavaScript, потому что код, который должен быть оценен, может быть изменен пользователем ).

Итак, когда можно использовать его?

Ричард Тернер
источник
5
Большинство библиотек JSON фактически не используют eval для защиты от угроз безопасности.
Шон Макмиллан
11
@Sean - JQuery и Prototype используют eval (JQuery использует его через новую функцию)
plodder
5
@plodder - Где вы берете информацию? jQuery использует собственный JSON.parse () начиная с 1.4 (еще в 1/2010)! Убедитесь
кен
3
«Очевидно, что нужно использовать eval () для разбора JSON» - это не так, наоборот - нельзя использовать eval для разбора JSON! Использование Дуглас Crockfords' (создатель JSON) json2.js скрипт json.org !
TMS
11
@ Томас ирония в том, что json2.js использует eval для анализа JSON
tobyodavies

Ответы:

262

Я хотел бы воспользоваться моментом, чтобы рассмотреть предпосылку вашего вопроса - это eval () - " зло ". Слово « зло », используемое людьми на языке программирования, обычно означает «опасный», или, точнее, «способный причинить много вреда простой командой». Итак, когда можно использовать что-то опасное? Когда вы знаете, в чем опасность, и когда вы принимаете соответствующие меры предосторожности.

Кстати, давайте посмотрим на опасности при использовании eval (). Вероятно, есть много небольших скрытых опасностей, как и все остальное, но есть два больших риска - причина, по которой eval () считается злом - это производительность и внедрение кода.

  • Производительность - eval () запускает интерпретатор / компилятор. Если ваш код скомпилирован, то это большой успех, потому что вам нужно вызывать возможно тяжелый компилятор во время выполнения. Тем не менее, JavaScript по-прежнему в основном интерпретируемый язык, что означает, что вызов eval () не является большим ударом по производительности в общем случае (но см. Мои конкретные замечания ниже).
  • Внедрение кода - eval () потенциально запускает строку кода с повышенными привилегиями. Например, программа, работающая как администратор / root, никогда не захочет eval () ввода пользователя, потому что этот ввод может быть «rm -rf / etc / Important-file» или хуже. Опять же, JavaScript в браузере не имеет этой проблемы, потому что программа все равно запускается под собственной учетной записью пользователя. JavaScript на стороне сервера может иметь эту проблему.

На ваш конкретный случай. Насколько я понимаю, вы сами генерируете строки, поэтому, если вы будете осторожны, чтобы не генерировать строку типа "rm -rf что-то важное", нет риска внедрения кода (но, пожалуйста, помните, это очень Трудно обеспечить это в общем случае). Кроме того, если вы работаете в браузере, то внедрение кода представляет собой довольно незначительный риск, я считаю.

Что касается производительности, вам придется сравнить ее с простотой кодирования. По моему мнению, если вы анализируете формулу, вы можете также вычислить результат во время анализа, а не запускать другой анализатор (тот, что внутри eval ()). Но это может быть проще для кода с использованием eval (), и снижение производительности, вероятно, будет незаметным. Похоже, что eval () в этом случае не более злой, чем любая другая функция, которая может сэкономить вам время.

user27476
источник
78
Вы не
решаете
48
Внедрение кода - это очень серьезная проблема для JavaScript, если вас вообще беспокоят данные вашего пользователя. Внедренный код будет работать (в браузере) так, как будто он пришел с вашего сайта, что позволяет ему выполнять любые действия, которые пользователь может выполнить вручную. Если вы разрешаете (стороннему) коду заходить на вашу страницу, он может заказывать товары от имени вашего клиента, менять его граватар или делать то, что они могут делать через ваш сайт. Будь очень осторожен. Предоставить хакерам право владеть вашими клиентами так же плохо, как позволить им владеть вашим сервером.
Шон Макмиллан
71
Если данные поступают с вашего сервера и это то, что вы, разработчик, сгенерировали, использование eval () не повредит. Настоящий вред - верить всему, что ты читаешь. Вы видите, что многие люди говорят, что eval () - зло, и они понятия не имеют, почему, кроме того, что они где-то читают.
Винс Пануччо
42
@Sean McMillan: Я хочу верить вам, но если кто-то собирается перехватывать и изменять javascript, идущий eval()с вашего сервера, он также может в первую очередь просто изменить источник страницы, а также взять под контроль информацию пользователя. , , Я не вижу разницы.
Уолт W
20
Re "Внедрение кода - ... Опять же, JavaScript в браузере не имеет такой проблемы" & "Кроме того, если вы работаете в браузере, то, как мне кажется, внедрение кода представляет собой довольно незначительный риск". Вы предполагаете, что внедрение кода в браузер не является проблемой? XSS уже три года подряд входит в тройку лучших в списке OWASP.
Майк Самуэль
72

eval()не зло Или, если это так, это зло так же, как отражение, файловый / сетевой ввод / вывод, многопоточность и IPC являются «злыми» в других языках.

Если для вашей цели , eval()быстрее , чем ручная интерпретация, или делают ваш код более простым, или более понятно ... то вы должны использовать его. Если ни то, ни другое Просто как тот.

Shog9
источник
5
Одной из таких целей может быть создание оптимизированного кода, который будет либо слишком длинным, либо слишком повторяющимся для написания вручную. Такие вещи, которые в LISP будут вызывать макрос.
wberry
5
Это такой общий совет, что его можно применять буквально к любому блоку кода, который существует. Это действительно ничего не добавляет к этому вопросу; в частности, это не помогает никому приходить сюда определить, является ли их конкретное использование проблематичным или нет.
jpmc26
2
Быстрее, проще, понятнее ... Этот ответ недостаточно хорошо охватывает последствия для безопасности.
Рууд
55

Когда вы доверяете источнику.

В случае JSON более или менее сложно вмешаться в источник, потому что он исходит от веб-сервера, которым вы управляете. Пока сам JSON не содержит данных, загруженных пользователем, нет существенного недостатка в использовании eval.

Во всех других случаях я бы сделал все возможное, чтобы пользовательские данные соответствовали моим правилам, прежде чем передавать их в eval ().

Томалак
источник
13
Строка json всегда должна проверяться на соответствие грамматике json перед ее использованием в eval (). Поэтому строка json "{foo: alert ('XSS')}" не будет проходить, поскольку «alert ('XSS')» не является правильным значением.
Гамбо
3
Ну, тогда используйте HTTPS. OTOH: «человек посередине» - это не типичный сценарий атаки для веб-приложения для сада, в то время как межсайтовый скриптинг есть.
Томалак
7
evalтакже не будет правильно анализировать все допустимые строки JSON. Например, JSON.parse(' "\u2028" ') === "\u2028"но eval(' "\u2028" ')вызывает исключение, потому что U + 2028 - это новая строка в JavaScript, но это не новая строка в отношении JSON.
Майк Сэмюэль
1
@Justin - если протокол скомпрометирован, обычно первоначальная загрузка страницы отправляется по тому же протоколу, а затем это спорный вопрос, потому что клиент уже настолько скомпрометирован, насколько это возможно.
Антином
1
Красиво сказал @ Томалак, я упомянул об этом в своем ответе прямо сейчас! Потрясающие!
NiCk Newman
25

Давайте настоящие люди:

  1. Каждый крупный браузер теперь имеет встроенную консоль, которую ваш потенциальный хакер может использовать в изобилии для вызова любой функции с любым значением - зачем им пытаться использовать оператор eval - даже если они могут?

  2. Если для компиляции 2000 строк JavaScript требуется 0,2 секунды, какова будет моя производительность, если я получу четыре строки JSON?

Даже объяснение Крокфорда «зло - это зло» слабо.

eval is Evil, функция eval является наиболее часто используемой функцией JavaScript. Избегай это

Как мог бы сам Крокфорд сказать: «Такое утверждение порождает иррациональный невроз. Не покупайте его».

Понимание eval и знание того, когда это может быть полезно, гораздо важнее. Например, eval - это разумный инструмент для оценки ответов сервера, сгенерированных вашим программным обеспечением.

Кстати: Prototype.js вызывает eval напрямую пять раз (в том числе в evalJSON () и evalResponse ()). jQuery использует его в parseJSON (через конструктор функций).

флегматичный
источник
10
JQuery использует встроенную в браузер функцию JSON.parse, если она доступна (что намного быстрее и безопаснее), используя eval только в качестве резервного механизма. Утверждение, что «зло - это зло», является достаточно хорошим ориентиром.
jjmontes
30
Re "Каждый крупный браузер теперь имеет встроенную консоль ...". Внедрение кода является проблемой, когда один пользователь может ввести код, который затем запускается в браузере другого пользователя. Консоли браузера сами по себе не позволяют одному пользователю запускать код в браузере других пользователей, поэтому они не имеют значения при принятии решения о целесообразности защиты от внедрения кода.
Майк Самуэль
28
«Каждый крупный браузер теперь имеет встроенную консоль ... зачем им использовать оператор eval?» - Вы далеко от цели. Я предлагаю вам отредактировать ответ. Способность одного пользователя вводить код, который может выполняться в браузере другого, является серьезной проблемой. И это то, где вы должны стать по-настоящему.
Akkishore
5
@akkishore, я буду признателен, если вы приведете пример из реальной жизни, который поддерживает ваши высказанные заявления.
Акаш Кава
7
@AkashKava Что вы не понимаете, так это то, что если я отправлю javascript в поле для комментариев, и этот javascript попадет в базу данных. Когда другой пользователь просматривает этот комментарий (в который я поместил javascript), eval возьмет этот javascript при его визуализации и оценит его с помощью интерпретатора, в результате чего мой встроенный javascript будет выполнен в браузере другого пользователя. Делая это, я могу собрать всю информацию. Их имя пользователя, идентификатор пользователя в базе данных, адрес электронной почты и т. Д. Это не сложный ответ, если бы у вас был Googled XSS, через 10 секунд вы бы увидели, почему это проблема.
Кайл Рихтер
18

Я склонен следовать советам Крокфорда в для eval(), и избежать его полностью. Даже способы, которые, кажется, требуют этого, не делают. Например, setTimeout()позволяет вам передавать функцию, а не Eval.

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

Даже если это надежный источник, я не использую его, потому что код, возвращаемый JSON, может быть искажен, что в лучшем случае может сделать что-то нехорошее, а в худшем - показать что-то плохое.

swilliams
источник
2
Я думаю, что ошибки в форматере JSON на стороне сервера, безусловно, являются проблемой. Зависит ли ответ от сервера от какого-либо пользовательского текста? Тогда ты должен смотреть на XSS.
swilliams
3
Если ваш веб-сервер не аутентифицирован через HTTPS, то вы можете подвергнуться некоторой атаке типа «человек посередине», когда другой хост перехватывает запрос и отправляет свои собственные данные.
Бен Комби
11
Если кто-то может выполнить атаку «человек посередине», он может легко внедрить что-либо в ваши сценарии.
el.pescado
10
Вы вообще не должны полагаться на свой код javascript ... Вы не должны полагаться на все, что работает на стороне клиента ... Если кто-то совершает атаку "человек посередине", зачем ему связываться с вашими объектами json? Он может предоставить вам другую веб-страницу и другие файлы
JS
5
Мне лично не нравится аргумент «всегда есть другие способы сделать это». Например, вы также можете сказать, что всегда есть способы избежать объектно-ориентированного программирования. Это не значит, что это не лучший вариант. Если вы понимаете eval и его опасность, это может быть отличным инструментом для использования в нужных ситуациях.
Даллин
4

Я видел, как люди выступают за то, чтобы не использовать eval, потому что это зло , но я видел, как те же люди динамически используют Function и setTimeout, поэтому они используют eval под капотами : D

Кстати, если ваша песочница недостаточно уверена (например, если вы работаете на сайте, где разрешено внедрение кода), eval - это последняя из ваших проблем. Основное правило безопасности заключается в том, что все вводимые данные являются злыми, но в случае JavaScript даже сам JavaScript может быть злым, поскольку в JavaScript вы можете перезаписать любую функцию и просто не можете быть уверены, что используете настоящую, поэтому если вредоносный код запускается раньше вас, вы не можете доверять любой встроенной функции JavaScript: D

Теперь эпилог к ​​этому посту:

Если вам ДЕЙСТВИТЕЛЬНО это нужно (80% времени eval НЕ требуется) и вы уверены в том, что делаете, просто используйте eval (или лучшую функцию;)), затворы и ООП покрывают 80/90% случай, когда eval может быть заменен с использованием другой логики, остальное - это динамически генерируемый код (например, если вы пишете интерпретатор) и, как вы уже сказали, оценка JSON (здесь вы можете использовать безопасную оценку Крокфорда;))

kentaromiura
источник
И, как указал сам Крокфорд , современные веб-браузеры имеют встроенную функцию JSON.parse .
Рууд
4

Eval дополняет компиляцию, которая используется в шаблонах кода. Под шаблонизацией я подразумеваю, что вы пишете упрощенный генератор шаблонов, который генерирует полезный код шаблона, который увеличивает скорость разработки.

Я написал фреймворк, в котором разработчики не используют EVAL, но они используют наш фреймворк, и, в свою очередь, этот фреймворк должен использовать EVAL для генерации шаблонов.

Производительность EVAL может быть увеличена с помощью следующего метода; вместо выполнения сценария вы должны вернуть функцию.

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

Это должно быть организовано как

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

var a = f(3,5);

Кеширование f, безусловно, улучшит скорость.

Также Chrome позволяет легко отлаживать такие функции.

Что касается безопасности, использование eval или нет вряд ли будет иметь значение,

  1. Во-первых, браузер вызывает весь сценарий в песочнице.
  2. Любой код, который является злом в EVAL, является злом в самом браузере. Злоумышленник или кто-либо другой может легко внедрить узел сценария в DOM и сделать что-нибудь, если он / она сможет что-либо проверить. Не использовать EVAL не будет иметь никакого значения.
  3. В основном это плохая защита на стороне сервера, которая вредна. Плохая проверка файлов cookie или плохая реализация ACL на сервере вызывают большинство атак.
  4. Недавно в нативном коде Java была уязвимость Java и т. Д. JavaScript был и предназначен для работы в песочнице, тогда как апплеты были разработаны для работы вне песочницы с сертификатами и т. Д., Которые приводят к уязвимостям и многим другим.
  5. Написание кода для имитации браузера не сложно. Все, что вам нужно сделать, это сделать HTTP-запрос к серверу с вашей любимой строкой агента пользователя. Все инструменты тестирования в любом случае имитируют браузеры; если злоумышленник хочет причинить вам вред, EVAL является их последним средством. У них есть много других способов справиться с вашей безопасностью на стороне сервера.
  6. DOM браузера не имеет доступа к файлам и не имеет имени пользователя. Фактически ничто на машине, которой eval не может дать доступ.

Если ваша серверная безопасность достаточно надежна, чтобы кто-то мог атаковать откуда угодно, вам не стоит беспокоиться о EVAL. Как я уже упоминал, если EVAL не существует, у злоумышленников есть много инструментов для взлома вашего сервера независимо от возможностей EVAL вашего браузера.

Eval хорош только для генерации некоторых шаблонов для выполнения сложной обработки строк на основе чего-то, что не используется заранее. Например, я предпочту

"FirstName + ' ' + LastName"

В отличие от

"LastName + ' ' + FirstName"

Как мое отображаемое имя, которое может исходить из базы данных и не является жестко закодированным.

Акаш Кава
источник
Вы можете использовать функцию вместо Eval - function (first, last) { return last + ' ' + first }.
Конрад Боровски
Названия столбцов взяты из базы данных.
Акаш Кава
3
Угроза в evalосновном других пользователей . Допустим, у вас есть страница настроек, и она позволяет вам установить, как ваше имя будет выглядеть для других. Скажем также, что вы не очень четко думали, когда писали это, поэтому в вашем окне выбора есть такие параметры, как <option value="LastName + ' ' + FirstName">Last First</option>. Я открываю инструменты разработчика, изменяю valueопцию на alert('PWNED!'), выбираю измененную опцию и отправляю форму. Теперь, когда кто-то другой увидит мое отображаемое имя, этот код запускается.
Чао
@cHao, тот пример, о котором вы говорите, является примером плохой безопасности на стороне сервера: сервер никогда не должен принимать данные, которые могут быть выполнены как код в чьем-либо браузере Еще раз, вы не поняли концепцию плохой безопасности на стороне сервера.
Акаш Кава
1
Вы можете жаловаться на безопасность на стороне сервера, если хотите, но весь смысл в evalтом, чтобы выполнить код, который не является частью написанного вами сценария. Если вам не нужна сила для этого (а вы почти никогда не делаете этого), избегание evalпомогает избежать целой категории проблем. Это хорошо, если ваш серверный код не идеален.
Цао
4

При отладке в Chrome (v28.0.1500.72) я обнаружил, что переменные не связаны с замыканиями, если они не используются во вложенной функции, которая создает замыкание. Я думаю, это оптимизация движка JavaScript.

НО : когда eval()используется внутри функции, которая вызывает замыкание, ВСЕ переменные внешних функций привязываются к замыканию, даже если они вообще не используются. Если у кого-то есть время, чтобы проверить, могут ли утечки памяти быть вызваны этим, пожалуйста, оставьте мне комментарий ниже.

Вот мой тестовый код:

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

Здесь я хотел бы отметить, что eval () не обязательно должен ссылаться на нативную eval()функцию. Все зависит от названия функции . Таким образом, при вызове натива eval()с псевдонимом (скажем, var noval = eval;а затем во внутренней функции noval(expression);) тогда оценка expressionможет потерпеть неудачу, когда она ссылается на переменные, которые должны быть частью замыкания, но на самом деле это не так.

Вениамин
источник
3

Нижняя граница

Если вы создали или санировали свой код eval, это никогда не было злом .

Чуть подробнее

evalэто зло , если работают на сервере , используя вход , представленный клиентом , который не создал разработчик или что было не продезинфицировать разработчик .

evalэто не зло , если он выполняется на клиенте, даже при использовании unsanitized ввода созданного клиента .

Очевидно, что вы всегда должны очищать входные данные, чтобы иметь некоторый контроль над тем, что потребляет ваш код.

аргументация

Клиент может выполнить любой произвольный код, который он хочет, даже если разработчик не кодировал его; Это верно не только для того, что уклоняется, но и призыв к evalсебе .

Стивен Спунгин
источник
2

Единственный случай, когда вы должны использовать eval (), это когда вам нужно запускать динамический JS на лету. Я говорю о JS, который вы загружаете асинхронно с сервера ...

... И 9 раз из 10 вы можете легко избежать этого путем рефакторинга.

Oli
источник
В настоящее время существуют другие (и лучшие) способы асинхронной загрузки JavaScript с сервера: w3bits.com/async-javascript
Рууд
1

evalредко является правильным выбором. В то время как могут быть многочисленные случаи, когда вы можете выполнить то, что вам нужно, объединяя сценарий и выполняя его на лету, в вашем распоряжении обычно гораздо более мощные и поддерживаемые методы: нотация ассоциативного массива ( obj["prop"]такая же, как obj.prop) , замыкания, объектно-ориентированные методы, функциональные методы - используйте их вместо этого.

yfeldblum
источник
1

Что касается клиентского сценария, я думаю, что вопрос безопасности является спорным. Все, что загружено в браузер, подвергается манипуляциям и должно рассматриваться как таковое. Использование оператора eval () практически исключает риск, когда есть гораздо более простые способы выполнения кода JavaScript и / или манипулирования объектами в DOM, такими как строка URL в вашем браузере.

javascript:alert("hello");

Если кто-то хочет манипулировать своим DOM, я говорю: откажитесь. Безопасность для предотвращения любого типа атаки всегда должна быть ответственностью серверного приложения, точка.

С прагматической точки зрения нет смысла использовать eval () в ситуации, когда все можно сделать иначе. Тем не менее, есть конкретные случаи, когда следует использовать eval. Когда это так, это определенно можно сделать без риска взорвать страницу.

<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 "Нет никакого риска в использовании оператора eval (), когда есть намного более простые способы выполнить JavaScript и / или манипулировать объектами в DOM". Внедрение кода является проблемой, когда один пользователь может ввести код, который затем запускается в браузере другого пользователя. Консоли браузера сами по себе не позволяют одному пользователю запускать код в браузере других пользователей, поэтому они не имеют значения при принятии решения о целесообразности защиты от внедрения кода.
Майк Сэмюэль
Не <head></head>требуется, даже если пусто?
Питер Мортенсен
2
Этот ответ полностью игнорирует риски XSS .
Рууд
1

На стороне сервера eval полезен при работе с внешними сценариями, такими как sql или influenxdb или mongo. Там, где пользовательская проверка во время выполнения может быть выполнена без повторного развертывания ваших служб.

Например, служба достижений со следующими метаданными

{
  "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\"}`"
  }
}

Которые затем позволяют,

  • Прямая инъекция объекта / значений через буквальную строку в формате json, полезная для шаблонных текстов

  • Можно использовать в качестве компаратора, скажем, мы устанавливаем правила, как проверять квест или события в CMS

Против этого:

  • Могут быть ошибки в коде и поломка вещей в сервисе, если они не полностью протестированы.

  • Если хакер может написать скрипт в вашей системе, то вы в значительной степени облажались.

  • Один из способов проверки вашего скрипта - хранить хеш ваших скриптов где-нибудь в безопасности, чтобы вы могли проверить их перед запуском.

MichaelC
источник
Ницца. Когда я задал вопрос, я даже не думал о серверной JS.
Ричард Тернер
1

Я думаю, что любые случаи оправданности eval были бы редкостью. Вы, скорее всего, будете использовать его, думая, что это оправдано, чем использовать его, когда это на самом деле оправдано.

Вопросы безопасности являются наиболее известными. Но также имейте в виду, что JavaScript использует JIT-компиляцию, и это плохо работает с eval. Eval чем-то напоминает «черный ящик» для компилятора, и JavaScript должен уметь предсказывать код заранее (до некоторой степени), чтобы безопасно и правильно применять оптимизацию производительности и область видимости. В некоторых случаях влияние на производительность может даже повлиять на другой код за пределами eval.

Если вы хотите узнать больше: https://github.com/getify/You-Dont-Know-JS/blob/master/scope%20%26%20closures/ch2.md#eval

Андре О
источник
0

Это нормально, если вы имеете полный контроль над кодом, который передается evalфункции.

Джон Топли
источник
2
Если у вас есть полный контроль над тем, что вы передаете eval, то возникает большой вопрос, когда имеет смысл, чтобы это была строка, а не реальный JS?
Чао
@cHao Например, если у вас большое игровое приложение (5-10MB Javascript), лучше сначала создать простой быстро загружаемый AJAX-Preloader (1kb), который загружает большой Main-Script и отображает Бар или что-то подобное. После загрузки вы можете использовать «eval (источник)» или, что лучше, «новую функцию (источник)» для запуска загруженного Game-Application-Script. Таким образом, пользователь может визуально увидеть, что приложению нужно время для загрузки, пока игра не запустится. Без этого пользователь должен ждать загрузки всего приложения без какой-либо визуальной обратной связи.
SammieFox
@SammieFox Есть и другие (и лучшие) способы сделать это, в первую очередь <script async="true" src="...">. Смотрите также: w3bits.com/async-javascript
Рууд
Ответ опасный совет; слишком много разработчиков имеют ложное чувство контроля. Этот совет имеет смысл для программного обеспечения, которое больше не поддерживается. Но такое программное обеспечение следует считать мертвым.
Рууд
0

Только во время тестирования, если это возможно. Также обратите внимание, что eval () намного медленнее, чем другие специализированные оценщики JSON и т. Д.

Эрик Венделин
источник
0

Нет причин не использовать eval (), если вы можете быть уверены, что источник кода исходит от вас или от реального пользователя. Несмотря на то, что он может манипулировать тем, что отправляется в функцию eval (), это не проблема безопасности, поскольку он может манипулировать исходным кодом веб-сайта и поэтому может изменить сам код JavaScript.

Итак ... когда не следует использовать eval ()? Eval () не должен использоваться, только когда есть вероятность, что третье лицо может изменить его. Например, перехватить соединение между клиентом и вашим сервером (но если это проблема, используйте HTTPS). Вы не должны использовать eval () для анализа кода, написанного другими людьми, как на форуме.

Георг Шолли
источник
Re "Нет причин не использовать eval (), если вы можете быть уверены, что источник кода исходит от вас или от реального пользователя". Это предполагает, что есть один пользователь. Эта предпосылка не указана в ФП. При наличии нескольких пользователей небрежное evalотношение к строке, составленной из содержимого одного пользователя, может позволить этому пользователю выполнять код в браузере другого пользователя.
Майк Сэмюэль
@MikeSamuel, eval может выполнять код в браузере другого пользователя, я не слышал этого, вы пробовали это? Этого никогда не было в истории просмотра. Можете ли вы показать нам пример?
Акаш Кава
@AkashKava, Строка может быть создана одним пользовательским агентом, сохранена в базе данных, а затем отправлена ​​в другой браузер, который evalее видит . Так происходит все время.
Майк Самуэль,
База данных @MikeSamuel? где? кто обслуживает неверную строку? не виновата ли база данных на стороне сервера? Во-первых, EVAL нельзя винить в плохом написании кода на стороне сервера. Используйте jsfiddle и покажите миру пример из реальной жизни, где он может причинить вред.
Акаш Кава
2
@AkashKava, я не понимаю твой вопрос. Мы говорим не о конкретном приложении, а о причинах не использоватьeval . Чем полезно винить сервер? Если кто-то должен быть обвинен, это должен быть нападающий. Независимо от вины, клиент, который не уязвим для XSS, несмотря на ошибки на сервере, лучше, чем клиент, который уязвим при прочих равных условиях.
Майк Сэмюэль
0

Если это действительно нужно, eval - это не зло. Но 99,9% случаев использования eval, с которыми я сталкиваюсь, не нужны (не считая setTimeout).

Для меня зло - это не производительность или даже проблема безопасности (ну, косвенно, это и то и другое). Все такие ненужные использования eval добавляют к адскому обслуживанию. Инструменты рефакторинга скинуты. Искать код сложно. Непредвиденные последствия этих уловок легион.

PEZ
источник
5
eval не требуется для setTimeout. Вы также можете использовать ссылку на функцию там.
Мэтью Крамли
0

Когда eval () JavaScript не является злом?

Я всегда пытаюсь отговорить от использования eval . Почти всегда доступно более чистое и удобное решение. Eval не нужен даже для анализа JSON . Eval добавляет в ад обслуживания . Недаром его осуждают такие мастера, как Дуглас Крокфорд.

Но я нашел один пример, где его следует использовать:

Когда вам нужно передать выражение.

Например, у меня есть функция , которая строит общий google.maps.ImageMapTypeобъект для меня, но я должен сказать ему рецепт, как он должен построить URL плитки из zoomи 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);
        },
        ....
    });
}
TMS
источник
3
Похоже, что это может быть реорганизовано, так что eval () не требуется - tileURLexpr - это просто шаблон, так что некоторое разумное использование replace () сделает эту работу. Тем не менее, это напоминает мне пример, который я имел в виду при представлении вопроса, который был связан с разрешением пользователю задавать математическую формулу для оценки, аналогично функциональности электронных таблиц. Конечно, я не упомянул об этом в то время, потому что не хотел влиять на ответы!
Ричард Тернер
8
tileURL: function (zoom, coord) { return 'http://tile.openstreetmap.org/' + b + '/' + a.x + '/' + a.y + '.png'; },
Кейси Чу
0

Мой пример использования eval: импорт .

Как это обычно делается.

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

Но с помощью evalнебольшой вспомогательной функции он выглядит намного лучше:

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

importable может выглядеть так (эта версия не поддерживает импорт конкретных элементов).

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 за идею, но у вас есть ошибка здесь: .replace(/name/g, name).replace('path', path). Если nameсодержит строку, "path"то вы можете получить сюрпризы.
Wberry
1
Объявление одной переменной для каждого свойства componentsявляется возможным запахом кода; рефакторинг вашего кода может полностью устранить «проблему». Ваше текущее решение - просто синтаксический сахар. Если вы настаиваете на этом, я бы порекомендовал написать собственный препроцессор, который будет выполнен перед развертыванием. Это должно держаться evalподальше от производственного кода.
Рууд
0

Эвал не злой, просто неправильно

Если вы создали код, входящий в него, или можете доверять ему, все в порядке. Люди продолжают говорить о том, что пользовательский ввод не имеет значения с eval. Ну вроде ~

Если есть пользовательский ввод, который отправляется на сервер, затем возвращается к клиенту, и этот код используется в eval без очистки. Поздравляю, вы открыли ящик Пандоры для отправки пользовательских данных кому бы то ни было.

В зависимости от того, где находится eval, многие веб-сайты используют SPA, и eval может упростить пользователю доступ к внутренним компонентам приложения, что в противном случае было бы нелегко. Теперь они могут создать поддельное расширение для браузера, которое может записывать данные в eval и снова красть данные.

Просто должен понять, какой смысл использовать Eval. Генерация кода на самом деле не идеальна, когда вы можете просто создавать методы для таких вещей, использовать объекты или тому подобное.

Теперь хороший пример использования eval. Ваш сервер читает файл swagger, который вы создали. Многие из параметров URL создаются в формате {myParam}. Таким образом, вы хотите прочитать URL-адреса, а затем преобразовать их в строки шаблона без необходимости выполнять сложные замены, поскольку у вас много конечных точек. Таким образом, вы можете сделать что-то вроде этого. Обратите внимание, что это очень простой пример.

const params = { id: 5 };

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

// use eval(route); to do something
jemiloii
источник
-1

Генерация кода. Недавно я написал библиотеку под названием Hyperbars, которая ликвидирует разрыв между виртуальным домом и рулем . Это делается путем анализа шаблона руля и преобразования его в гиперскрипт . Гиперскрипт сначала генерируется в виде строки, а перед возвращением eval()превращается в исполняемый код. я нашелeval() в этой конкретной ситуации полную противоположность зла.

В основном из

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

К этому

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

Производительность eval() не является проблемой в подобной ситуации, потому что вам нужно только один раз интерпретировать сгенерированную строку, а затем многократно использовать исполняемый вывод.

Вы можете увидеть , как генерация кода было достигнуто , если вам интересно здесь .

Wikened
источник
«Гиперскрипт сначала генерируется в виде строки (...)». Имеет больше смысла делать все генерации кода на этапе сборки, записывать полученный код гиперсценария в отдельный исполняемый файл (.js), затем развертывать этот файл для тестирования и производство. Мне нравится, как вы используете генерацию кода. Это просто evalнамек на то, что некоторая ответственность, которая принадлежит во время компиляции, перешла во время выполнения.
Рууд
-1

Я считаю, что eval - очень мощная функция для клиентских веб-приложений и безопасная ... Такая же безопасная, как и JavaScript, а это не так. :-) Проблемы безопасности, по сути, являются проблемой на стороне сервера, потому что теперь, с помощью такого инструмента, как Firebug, вы можете атаковать любое приложение JavaScript.

Питер Мортенсен
источник
1
Использование evalдолжно быть защищено от атак XSS, что не всегда легко сделать правильно.
Бенджамин
-1

Eval полезен для генерации кода, когда у вас нет макросов.

Для (глупого) примера, если вы пишете компилятор Brainfuck , вы, вероятно, захотите создать функцию, которая выполняет последовательность инструкций в виде строки, и вычислить ее для возврата функции.

Эрик Халевич
источник
Либо вы пишете компилятор (который сохраняет, а не выполняет генерируемый код), либо вы пишете интерпретатор (где каждая инструкция имеет предварительно скомпилированную реализацию). Ни один из вариантов использования для eval.
Рууд
Если вы сгенерировали код javascript и хотели немедленно его выполнить (скажем, для повышения производительности по сравнению с прямой интерпретацией), это был бы вариант использования для eval.
Эрик Халевич
Хорошая точка зрения; Я видел пример в этой статье о Blockly . Я в шоке, что Google рекомендует eval, когда альтернатива ( функция ) является одновременно более быстрой ( как описано в MDN ) и более надежной (предотвращает непредсказуемые ошибки благодаря лучшей изоляции между сгенерированным кодом и другим «вспомогательным» кодом на той же веб-странице).
Рууд
-5

Когда вы анализируете структуру JSON с помощью функции синтаксического анализа (например, jQuery.parseJSON), она ожидает идеальную структуру файла JSON (каждое имя свойства в двойных кавычках). Тем не менее, JavaScript является более гибким. Поэтому вы можете использовать eval (), чтобы избежать этого.

vitmalina
источник
4
Не используйте вслепую eval, особенно при получении данных JSON из стороннего источника. Смотрите JSON.Stringify без кавычек на свойства? для правильного подхода к разбору "JSON без кавычек имен ключей".
Роб W
2
Если он не использует двойные кавычки вокруг имен свойств, это может быть строковое представление литерала объекта, но это не JSON . JSON определяет имена свойств как a stringи определяет последовательность stringкак ноль или более символов Unicode, заключенных в двойные кавычки, с использованием обратной косой черты.
бесполезный код
Смотрите статью Николаса Закаса - «eval () - это не зло, а просто неправильно» nczonline.net/blog/2013/06/25/eval-isnt-evil-just-misunderstood
vitmalina
@vitmalina Из статьи Закаса: «Это может быть опасно, если вы берете пользовательский ввод и запускаете его через eval (). Однако, если ваш ввод не от пользователя, есть ли реальная опасность?» Это именно проблема. Как только ваш код выходит за рамки «привет мира», быстро становится невозможным доказать, что вы не пропускаете пользовательский ввод eval. В любом серьезном мультитенантном веб-приложении с десятками разработчиков, работающих над одной и той же кодовой базой, это недопустимо.
Рууд