Коли JavaScript eval () не злий?


263

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

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

Отже, коли це нормально використовувати?


5
Більшість бібліотек JSON насправді не використовують eval під кришкою, саме для захисту від ризиків для безпеки.
Шон Макміллан

11
@Sean - І JQuery, і прототип використовують eval (JQuery використовує це за допомогою нової функції)
plodder

5
@plodder - Де ви отримуєте свою інформацію? jQuery використовує вбудований JSON.parse () з 1.4 (шлях назад у 1/2010)! Бачите самі: code.jquery.com/jquery-1.4.js
кен

3
"Очевидно, треба використовувати eval () для розбору JSON" - це неправда, навпаки - не слід використовувати eval для розбору JSON! Використовуйте сценарій json2.js Дугласа Крокфордса (творець JSON) від json.org !
TMS

11
@Тома іронія в тому, що json2.js використовує eval для розбору JSON
tobyodavies

Відповіді:


262

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

До речі, давайте розглянемо небезпеку використання eval (). Мабуть, існує багато невеликих прихованих небезпек, як і все інше, але два великі ризики - причину, через яку eval () вважається злом, - це продуктивність та введення коду.

  • Продуктивність - eval () виконує інтерпретатор / компілятор. Якщо ваш код складений, то це великий удар, тому що вам потрібно викликати можливо важкого компілятора в середині виконання. Однак JavaScript все ще є переважно інтерпретованою мовою, а це означає, що виклик eval () не є великим успіхом у загальній справі (але дивіться мої конкретні зауваження нижче).
  • Введення коду - eval () потенційно виконує рядок коду під підвищеними привілеями. Наприклад, програма, що працює як адміністратор / root, ніколи не захоче eval () вводити користувача, тому що цей вхід потенційно може бути "rm -rf / etc / важливий файл" або гірше. Знову ж таки, JavaScript у браузері не має такої проблеми, оскільки програма все одно працює у власному обліковому записі користувача. На серверному JavaScript може виникнути ця проблема.

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

Що стосується продуктивності, то вам доведеться зважити це проти простоти кодування. На мою думку, якщо ви аналізуєте формулу, ви можете також обчислити результат під час розбору, а не запустити інший аналізатор (той, що знаходиться в eval ()). Але кодування за допомогою eval () може бути простіше, і хіт продуктивності, ймовірно, буде непомітний. Схоже, що eval () в цьому випадку не є більш злим, ніж будь-яка інша функція, яка, можливо, може заощадити ваш час.


78
Ви не
вирішуєте

48
Введення коду є дуже серйозною проблемою для JavaScript, якщо ви взагалі турбуєтесь про дані свого користувача. Введений код буде працювати (у веб-переглядачі) так, ніби він прийшов з вашого сайту, дозволяючи йому робити будь-який тип шенаніган, який користувач може робити вручну. Якщо ви дозволите (сторонній) код зайти на вашу сторінку, він може замовити речі від імені вашого клієнта або змінити його граватар або все, що вони могли зробити через ваш сайт. Будьте дуже обережні. Дозволити хакерам володіти вашими клієнтами так само погано, як дозволити їм володіти вашим сервером.
Шон Макміллан

71
Якщо дані надходять з вашого сервера і щось таке, що ви створили ви, розробник, немає ніякої шкоди у використанні eval (). Справжня шкода - це віра в усе, що ти читаєш. Ви бачите, що багато людей кажуть, що eval () - це зло, і вони не мають поняття, чому, крім того, що десь його читають.
Вінс Пануччо

42
@Sean McMillan: Я хочу вам повірити, але якщо хтось збирається перехоплювати та змінювати JavaScript, що переходить на eval()ваш сервер, він також може просто змінити джерело сторінки в першу чергу, а також взяти під контроль інформацію про користувача. . . Я не бачу різниці.
Walt W

20
Повторно "Введення коду - ... Знову ж таки, JavaScript у браузері не має такої проблеми" і "Крім того, якщо ви працюєте в браузері, тоді, я вважаю, введення коду є незначним ризиком". Ви припускаєте, що введення коду в браузер не є проблемою? XSS вже кілька років працює в трійці найпопулярніших вульнів у списку найкращих 10 OWASP.
Майк Самуель

72

eval()не зло. Або, якщо це так, це зло так само, як відображення, введення / виведення файлів / мережі, нарізка ниток та IPC є "злом" іншими мовами.

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


5
Однією з таких цілей може бути створення оптимізованого коду, який буде або занадто довгим, або занадто повторюваним, щоб писати вручну. Такий матеріал, який у LISP закликав би макрос.
wberry

5
Це така загальна порада, що вона може бути застосована до буквально будь-якого блоку коду, який існує. Це насправді нічого не додає до цього питання; зокрема, це не допомагає тим, хто приїжджає сюди, визначити, чи є їх конкретне використання проблематичним чи ні.
jpmc26

2
Швидше, простіше, зрозуміліше ... Ця відповідь не висвітлює наслідки безпеки.
Рууд Гельдерман

55

Коли ви довіряєте джерелу.

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

У всіх інших випадках я намагаюся забезпечити відповідність даних, що надаються користувачем, моїм правилам перед тим, як подавати їх до eval ().


13
Рядок json завжди повинен бути протестований проти граматики json, перш ніж використовувати його в eval (). Таким чином, рядок json "{foo: alert ('XSS')}" не пройде, оскільки "alert (" XSS ")" не є належним значенням.
Gumbo

3
Ну, тоді використовуйте HTTPS. OTOH: людина-посередині не є типовим сценарієм нападу для веб-додатків для садових сортів, тоді як сценарій крос-сайтів є.
Томалак

7
evalтакож не буде правильно аналізувати всі дійсні рядки JSON. Наприклад, JSON.parse(' "\u2028" ') === "\u2028"але eval(' "\u2028" ')створює виняток, оскільки U + 2028 - це новий рядок у JavaScript, але це не новий рядок, що стосується JSON.
Майк Самуель

1
@Justin - якщо протокол порушений, ну, як правило, початкове завантаження сторінки було б надіслано через той самий протокол, і тоді це суперечка, оскільки клієнт вже настільки ж скомпрометований, як це можливо.
antinome

1
Чудово сказав @Tomalak, я про це вже згадував у своїй відповіді! Дивовижно!
NiCk Newman

25

Давайте отримаємо справжніх людей:

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

  2. Якщо для складання 2000 рядків JavaScript потрібно 0,2 секунди, що таке зниження продуктивності, якщо я оцінюю чотири рядки JSON?

Навіть пояснення Крокфорда про "eval - зло" є слабким.

eval - Зло, функція eval - це найбільш нецільова функція JavaScript. Уникайте цього

Як може сказати сам Крокфорд, "подібне твердження має породження ірраціонального неврозу. Не купуйте цього".

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

BTW: Prototype.js викликає eval безпосередньо п'ять разів (у тому числі в evalJSON () та evalResponse ()). jQuery використовує його в parseJSON (через конструктор функцій).


10
JQuery використовує вбудовану в браузер функцію JSON.parse, якщо вона доступна (що набагато швидше і безпечніше), використовуючи eval лише як механізм резервного копіювання. Заява «евал є злом» є досить хорошим орієнтиром.
jjmontes

30
Знову "Кожен великий браузер тепер має вбудовану консоль ...". Введення коду - це проблема, коли один користувач може ввести код, який потім запускається в браузері іншого користувача. Консолі браузера самі по собі не дозволяють одному користувачу запускати код в іншому браузері користувачів, тому вони не мають значення при вирішенні питання про те, чи варто захищати від введення коду.
Майк Самуель

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, може бути пошкодженим, що може в кращому випадку зробити щось вибагливе, в гіршому випадку, викрити щось погане.


2
Я думаю, що помилки у форматі JSON на стороні сервера, безумовно, є проблемою. Чи залежить відповідь від сервера від будь-якого тексту, поданого користувачем? Тоді вам слід спостерігати за XSS.
swilliams

3
Якщо ваш веб-сервер не проходить автентифікацію за допомогою HTTPS, ви можете зазнати певної атаки "людина-в-середині", коли інший хост перехоплює запит і надсилає власні дані.
Бен Комбі

11
Якщо хтось може здійснити атаку "людина-посередині", він може легко ввести що-небудь у ваші сценарії.
el.pescado

10
Ви взагалі не повинні покладатися на свій код JavaScript ... Ви не покладаєтесь ні на що, що працює на стороні клієнта ... Якщо хтось робить атаку "людина-в-середині", то чому б він возився з вашими об'єктами json? Він може подавати вам іншу веб-сторінку та різні js-файли ...
Кальмарій

5
Мені особисто не подобається аргумент "завжди є інші способи зробити це". Наприклад, ви також можете сказати, що завжди є способи уникнути об'єктно-орієнтованого програмування. Це не означає, що це не чудовий варіант. Якщо ви розумієте eval і небезпеку, це може бути чудовим інструментом для використання в правильних ситуаціях.
Даллін

4

Я бачив людей, які захищають не використовувати eval, тому що це зло , але я бачив, що ті ж люди динамічно використовують функцію та setTimeout, тому вони використовують eval під капотами : D

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

Тепер епілогом до цієї публікації є:

Якщо вам дійсно це потрібно (80% часу eval НЕ потрібен), і ви впевнені в тому, що робите, просто використовуйте eval (або краще функцію;)), закриття та OOP покривають 80/90% у випадку, коли eval можна замінити за допомогою іншого виду логіки, решта - це динамічно генерований код (наприклад, якщо ви пишете перекладача) і, як ви вже говорили, оцінюючи JSON (тут ви можете використовувати безпечну оцінку Crockford;))


І як вказував сам Крокфорд , нинішні веб-браузери мають вбудовану функцію 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

@cHao, той, про який ви говорите, є прикладом поганої безпеки на сервері, сервер ніколи не повинен приймати дані, які можуть бути виконані як код у будь-якому браузері. Ще раз ви не зрозуміли поняття про слабку безпеку на стороні сервера.
Акаш Кава

1
Якщо вам подобається, ви можете зауважити про безпеку на стороні сервера, але вся справа в evalтому, щоб виконати код, який не входить до написаного вами сценарію. Якщо для цього вам не потрібні сили (а ви майже ніколи цього не робите), уникнення evalдопомагає вирішити цілу категорію проблем. Це добре, якщо ваш код на стороні сервера є менш ніж ідеальним.
cHao

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не є злом, якщо працює на клієнті, навіть якщо використовується несанізований вхід, створений клієнтом .

Очевидно, що ви завжди повинні санітувати дані, щоб мати певний контроль над тим, що витрачає ваш код.

Обґрунтування

Клієнт може запустити будь-який довільний код, який він хоче, навіть якщо розробник не кодував його; Це справедливо не тільки для того, що ухиляється, але і заклик до evalсебе .


2

Єдиний випадок, коли вам слід використовувати eval (), це коли вам потрібно запускати динамічний JS на льоту. Я говорю про JS, який ви асинхронно завантажуєте з сервера ...

... І 9 разів з 10 ви могли легко уникнути цього, рефакторинг.


На сьогоднішній день існують інші (і кращі) способи завантаження JavaScript асинхронно з сервера: w3bits.com/async-javascript
Рууд Гельдерман

1

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


1

Що стосується сценарію клієнта, я думаю, що питання безпеки є суперечливим моментом. Все, що завантажується в браузер, підлягає маніпуляції, і до цього слід ставитися. При використанні оператора eval () немає нульового ризику, коли є набагато простіші способи виконання коду JavaScript та / або маніпулювання об'єктами в DOM, наприклад, рядок URL у вашому браузері.

javascript:alert("hello");

Якщо хтось хоче маніпулювати їхнім DOM, я кажу розмахуючи. Захист для запобігання будь-якого типу атаки завжди повинен відповідати серверній програмі, періоду.

З прагматичної точки зору, використовувати 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 або influxdb або 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

Підтвердження цього:

  • Можуть бути помилки в коді і порушити речі в сервісі, якщо вони не повністю перевірені.

  • Якщо хакер може написати сценарій у вашій системі, то ви в значній мірі перекручені.

  • Один із способів перевірити свій скрипт - зберігати хеш сценаріїв десь у безпеці, тому ви можете перевірити їх перед запуском.


Приємно. Коли я задав питання, я навіть не думав про 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

@cHao Наприклад, якщо у вас є велика гра-додаток (5-10 МБ Javascript), краще створити спочатку простий швидкий завантажувач AJAX-Preloader (1kb), який завантажує великий Main-Script, відображаючи при цьому режим завантаження- Бар або щось подібне. Після завантаження ви можете використовувати "eval (джерело)" або краще "нову функцію (джерело)" для запуску завантаженої гри-додатка-скрипта. Таким чином користувач візуально бачить, що Додаток потребує часу для завантаження до запуску гри. Без цього користувачеві доводиться чекати, коли вся програма завантажиться без візуальних відгуків.
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 додають пекла технічного обслуговування. Інструменти для реконструкції скидаються. Пошук коду важкий. Непередбачуваними наслідками цих овалів є легіон.


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

0

Коли JavaScript eval () не злий?

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

Але я знайшов один приклад, де його слід використовувати:

Коли вам потрібно передати вираз.

Наприклад, у мене є функція, яка будує 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);
        },
        ....
    });
}

3
Це виглядає так, що це може бути відновлено, так що eval () не потрібен - tileURLexpr - це лише шаблон, тому деяке розумне використання замінника () зробить цю роботу. І все-таки це нагадує мені приклад, який я мав на увазі, коли я подав запитання, яке було пов'язано з тим, щоб дозволити користувачеві вказати математичну формулу, яку слід оцінювати, подібно до функціональності електронних таблиць. Звичайно, я цього часу не згадував, бо не хотів впливати на відповіді!
Річард Тернер

8
tileURL: function (zoom, coord) { return 'http://tile.openstreetmap.org/' + b + '/' + a.x + '/' + a.y + '.png'; },
Кейсі Чу

0

Мій приклад використання eval: import .

Як це зазвичай робиться.

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. Ваш сервер читає створений вами файл. Багато параметрів URL створено у форматі {myParam}. Отже, ви хочете прочитати URL-адреси, а потім перетворити їх у рядки шаблону без необхідності робити складні заміни, оскільки у вас є багато кінцевих точок. Тож ви можете зробити щось подібне. Зауважте, це дуже простий приклад.

const params = { id: 5 };

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

// use eval(route); to do something

-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() не є проблемою у подібній ситуації, тому що вам потрібно лише один раз інтерпретувати згенеровану рядок, а потім багато разів використовувати повторно виконаний файл.

Ви можете побачити, як було досягнуто створення коду, якщо вам цікаво тут .


"Гіперскрипт формується спочатку як рядок (...)" Має більше сенсу робити все генерування коду на фазі збірки, записувати отриманий код гіперскрипту в окремий виконуваний (.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 (), щоб уникнути цього.


4
Не використовуйте сліпо eval, особливо при отриманні даних JSON від стороннього джерела. Дивіться JSON.Stringify без лапок про властивості? за правильний підхід до розбору "JSON без цитованих ключових імен".
Роб Ш

2
Якщо він не використовує подвійні лапки навколо імен властивостей, це може бути рядкове представлення об'єкта буквально, але це не JSON . JSON визначає назви властивостей як a stringі визначає stringяк послідовність нульових або більше символів Unicode, загорнутих у подвійні лапки, використовуючи зворотні косої риски.
Марний кодекс

Дивіться статтю Ніколяса Закаса - "eval () не зло, просто неправильно зрозуміли" nczonline.net/blog/2013/06/25/eval-isnt-evil-just-misunder зрозуміло
vitmalina

@vitmalina З статті Закаса: "Це може бути небезпечно, якщо ви приймаєте введення користувача та запускаєте його через eval (). Однак, якщо ваш вклад не від користувача, чи існує реальна небезпека?" Саме в цьому і полягає проблема. Після того, як ваш код зросте за розмірами "привіт світ", швидко стає неможливо довести, що ви не просочуєтесь користувачем eval. У будь-якому серйозному веб-додатку для багатьох орендарів, де десятки розробників працюють на одній базі коду, це неприпустимо.
Рууд Гельдерман
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.