Функція eval - це потужний і простий спосіб динамічного генерування коду, тож які застереження?
Функція eval - це потужний і простий спосіб динамічного генерування коду, тож які застереження?
Відповіді:
Неправильне використання eval відкриває ваш код для ін'єкційних атак
Налагодження може бути складнішим (без номерів рядків тощо)
Код eval'd виконується повільніше (немає можливості компілювати / кешувати код eval'd)
Редагувати: Як в коментарях зазначає @Jeff Walden, сьогодні №3 є менш правдивим, ніж це було у 2008 році. Однак, хоча може відбуватися кешування скомпільованих сценаріїв, це обмежується лише сценаріями, які повторюються без змін. Більш вірогідний сценарій полягає в тому, що ви є сценаріями eval'ing, які щоразу зазнавали незначних змін і не могли бути кешовані. Скажімо, що ДЕЯКІЙ код eval'd виконується повільніше.
badHackerGuy'); doMaliciousThings();
а якщо ви візьмете моє ім’я користувача, сформулюйте його в якийсь сценарій і оцініть його в інших браузерах, то я можу запускати будь-який javascript, який я хочу на своїх машинах (наприклад, змушувати їх ставити +1 до моїх публікацій, розмістити свої дані на моєму сервері тощо)
debugger;
заяву до вихідного коду. Це зупинить виконання вашої програми в цьому рядку. Потім після цього ви можете додати точки налагодження налагодження, як це був лише інший файл JS.
eval не завжди є злом. Бувають випадки, коли це цілком доречно.
Однак зараз eval і історично масово переживають люди, які не знають, що роблять. Це, на жаль, включає людей, які пишуть підручники JavaScript, а в деяких випадках це дійсно може мати наслідки для безпеки - або, що найчастіше, прості помилки. Отже, чим більше ми можемо зробити, щоб кинути знак питання над eval, тим краще. Кожен раз, коли ви використовуєте eval, вам потрібно з розумом перевірити, що ви робите, тому що, швидше за все, ви можете зробити це кращим, безпечнішим і чистішим способом.
Щоб навести занадто типовий приклад, встановити колір елемента з ідентифікатором, збереженим у змінній "картопля":
eval('document.' + potato + '.style.color = "red"');
Якби автори цього виду коду мали уявлення про основи роботи об’єктів JavaScript, вони зрозуміли б, що квадратні дужки можуть використовуватися замість буквальних точок-імен, що заперечує необхідність eval:
document[potato].style.color = 'red';
... що набагато простіше читати, а також менш потенційно баггі.
(Але тоді хтось, хто / насправді / знав, що вони роблять, скаже:
document.getElementById(potato).style.color = 'red';
що є більш надійним, ніж хитрий старий трюк доступу до елементів DOM прямо з об’єкта документа.)
JSON.parse()
замість eval()
JSON?
Я вважаю, це тому, що він може виконувати будь-яку функцію JavaScript з рядка. Використовуючи це, людям стає простіше вводити в програму негідний код.
На думку спадають два моменти:
Безпека (але поки ви створюєте рядок, який потрібно оцінювати самостійно, це може бути проблемою)
Продуктивність: поки код, який потрібно виконати, невідомий, його неможливо оптимізувати. (про javascript та продуктивність, безумовно , презентація Стіва Йегге )
eval
а потім цей маленький фрагмент коду працює у веб-переглядачі B
Передача користувача вводу до eval () - це ризик безпеки, але також кожне виклик eval () створює новий екземпляр інтерпретатора JavaScript. Це може бути ресурсна свиня.
В основному, це набагато складніше в обслуговуванні та налагодженні. Це як а goto
. Ви можете використовувати його, але це ускладнює пошук проблем і важче для людей, яким, можливо, доведеться пізніше внести зміни.
Слід пам’ятати, що ви часто можете використовувати eval () для виконання коду в іншому середовищі з обмеженим доступом - сайти соціальних мереж, які блокують певні функції JavaScript, іноді можна обдурити, розбивши їх у блоці eval -
eval('al' + 'er' + 't(\'' + 'hi there!' + '\')');
Тож якщо ви хочете запустити якийсь код JavaScript, де він інакше не може бути дозволений ( Myspace , я дивлюся на вас ...), то eval () може стати корисною хитрістю.
Однак, з усіх вищезгаданих причин, ви не повинні використовувати його для власного коду, де ви маєте повний контроль - це просто не потрібно, і краще перенести на полицю "хитрі JavaScript хакі".
[]["con"+"struc"+"tor"]["con"+"struc"+"tor"]('al' + 'er' + 't(\'' + 'hi there!' + '\')')()
Якщо ви не дозволите eval () динамічному контенту (через cgi або вхід), він настільки ж безпечний і надійний, як і всі інші JavaScript на вашій сторінці.
Поряд з рештою відповідей, я не думаю, що твердження eval можуть мати просунуте мінімізацію.
Це можливий ризик безпеки, він має інший обсяг виконання і є досить неефективним, оскільки створює абсолютно нове сценарій середовища для виконання коду. Дивіться тут для отримання додаткової інформації: eval .
Це дуже корисно, однак, і використання при помірності може додати багато хорошої функціональності.
Якщо ви не впевнені на 100%, що код, який оцінюється, отриманий з надійного джерела (зазвичай це ваша власна програма), то це надійний спосіб піддавати вашій системі напад сценаріїв між сайтом.
Я знаю, що це обговорення давнє, але мені дуже подобається такий підхід від Google і хотів поділитися цим почуттям з іншими;)
Інша справа, що чим краще ти отримуєш більше, тим більше намагаєшся зрозуміти, і нарешті ти просто не віриш, що щось добре чи погано тільки тому, що хтось так сказав :) Це дуже надихаюче відео, яке допомогло мені більше подумати над собою :) ДОБРІ ПРАКТИКИ хороші, але не використовуйте їх непомітно :)
Це не обов'язково так погано, якщо ви знаєте, в якому контексті ви його використовуєте.
Якщо ваша програма використовує eval()
для створення об’єкта з якогось JSON, який повернувся з XMLHttpRequest на ваш власний сайт, створений вашим надійним кодом на стороні сервера, це, мабуть, не проблема.
Ненадійний JavaScript-код на стороні клієнта так чи інакше не може зробити. За умови, що справа, яку ви виконуєте eval()
, походить з розумного джерела, у вас все добре.
Це значно знижує рівень вашої впевненості щодо безпеки.
Якщо ви хочете, щоб користувач вводив деякі логічні функції і оцінював ІЛІ АБО, то функція JavaScript eval є ідеальною. Я можу прийняти дві струни і eval(uate) string1 === string2
т.д.
Якщо ви помітили використання коду eval () у своєму коді, запам'ятайте мантру "eval () - це зло".
Ця функція займає довільну рядок і виконує її як код JavaScript. Коли код, про який йде мова, заздалегідь відомий (не визначається під час виконання), немає підстав використовувати eval (). Якщо код динамічно генерується під час виконання, часто існує кращий спосіб досягти мети без eval (). Наприклад, краще та простіше використовувати позначення квадратних дужок для доступу до динамічних властивостей:
// antipattern
var property = "name";
alert(eval("obj." + property));
// preferred
var property = "name";
alert(obj[property]);
Використання eval()
також має наслідки для безпеки, тому що ви можете виконувати код (наприклад, що надходить з мережі), який був підроблений. Це звичайний антипаттерн, коли стосується відповіді JSON на запит Ajax. У цих випадках краще використовувати вбудовані методи веб-переглядачів, щоб проаналізувати відповідь JSON, щоб переконатися, що вона безпечна та дійсна. Для браузерів, які не підтримують JSON.parse()
вбудовану систему, ви можете використовувати бібліотеку з JSON.org.
Також важливо пам’ятати, що передавання рядків до setInterval()
, setTimeout()
і Function()
конструктор здебільшого схоже на використання, eval()
і тому його слід уникати.
За лаштунками JavaScript все ще повинен оцінити та виконати рядок, який ви передаєте як код програмування:
// antipatterns
setTimeout("myFunc()", 1000);
setTimeout("myFunc(1, 2, 3)", 1000);
// preferred
setTimeout(myFunc, 1000);
setTimeout(function () {
myFunc(1, 2, 3);
}, 1000);
Використання нового конструктора Function () подібне до eval () і до нього слід підходити обережно. Це може бути потужною конструкцією, але часто не використовується. Якщо ви абсолютно повинні використовувати eval()
, ви можете замість цього використати нову функцію ().
Існує невелика потенційна вигода, оскільки код, оцінений у новій функції (), буде працювати в локальній області функцій, тому будь-які змінні, визначені з var у коді, що оцінюється, не стануть глобальними автоматично.
Інший спосіб запобігання автоматичних глобальних мереж -
eval()
перетворити виклик у негайну функцію.
Крім можливих проблем із безпекою, якщо ви виконуєте код, поданий користувачем, більшість випадків існує кращий спосіб, який не передбачає повторного розбору коду при кожному його виконанні. Анонімні функції або властивості об'єкта можуть замінити більшість застосувань eval і набагато безпечніші та швидші.
Це одна з хороших статей, яка розповідає про eval і як це не зло: http://www.nczonline.net/blog/2013/06/25/eval-isnt-evil-just-misunderpted/
Я не кажу, що ви повинні вибігати і починати використовувати eval () скрізь. Насправді, дуже мало випадків корисного використання для запуску eval (). Однозначно виникають проблеми з чіткістю коду, налагоджуваністю і, безумовно, продуктивністю, яку не слід нехтувати. Але вам не варто боятися використовувати його, коли у вас є випадок, коли eval () має сенс. Спробуйте не використовувати його спочатку, але не дозволяйте нікому лякати вас, думаючи, що ваш код є більш крихким або менш захищеним, коли eval () використовується належним чином.
eval () дуже потужний і може використовуватися для виконання оператора JS або оцінки виразу. Але питання не стосується використання eval (), а давайте лише скажемо, як на рядок, на який ви працюєте з eval (), впливає зловмисна сторона. Зрештою, у вас буде запущений шкідливий код. З владою виходить велика відповідальність. Тож використовуйте його розумно - це ви використовуєте. Це не дуже пов’язано з функцією eval (), але ця стаття має досить гарну інформацію: http://blogs.popart.com/2009/07/javascript-injection-attacks/ Якщо ви шукаєте основи eval () дивіться тут: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval
У JavaScript Engine є ряд оптимізацій продуктивності, які він виконує на етапі компіляції. Деякі з них зводяться до того, щоб мати змогу фактично статично проаналізувати код під час його використання та заздалегідь визначити, де знаходяться всі декларації змінної та функції, щоб зайняти менше зусиль для вирішення ідентифікаторів під час виконання.
Але якщо двигун знайде в коді eval (..), він, по суті, повинен припустити, що все його усвідомлення місця розташування ідентифікатора може бути недійсним, оскільки він не може знати в той час, який саме код ви можете передати в eval (..) щоб змінити лексичну область чи вміст об'єкта, з яким ви можете перейти, щоб створити нову лексичну область, з якою слід звертатися.
Іншими словами, в песимістичному розумінні більшість тих оптимізацій, які вона зробила б, безглуздо, якщо є eval (..), тому вона просто не виконує оптимізації.
Це все пояснює.
Довідка:
https://github.com/getify/You-Dont-Know-JS/blob/master/scope%20&%20closures/ch2.md#eval
https://github.com/getify/You-Dont-Know-JS/blob/master/scope%20&%20closures/ch2.md#performance
Це не завжди погана ідея. Візьмемо для прикладу генерацію коду. Нещодавно я написав бібліотеку під назвою Hyperbars, яка усуває розрив між віртуальним домом та рульовим рулем . Це робиться шляхом аналізу шаблону рулі та перетворення його в гіперскрипт, який згодом використовується virtual-dom. Гіперскрипт генерується спочатку як рядок, а перед поверненням 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()
не є проблемою в подібній ситуації, тому що вам потрібно лише один раз інтерпретувати згенеровану рядок, а потім багато разів використовувати повторно виконаний файл.
Ви можете побачити, як було досягнуто створення коду, якщо вам цікаво тут .
Я б сказав, що це неважливо, якщо ви використовуєте eval()
javascript, який запускається в браузерах. * (Нюанс)
У всіх сучасних браузерах є консоль розробника, де ви можете в будь-якому випадку виконати довільний javascript, і будь-який напіврозумний розробник може переглядати ваш джерело JS і вводити в консоль розробника всі необхідні для нього шматочки.
* Поки кінцеві точки вашого сервера мають правильну перевірку та санітацію значень, що надаються користувачем, це не має значення, що аналізується та eval'd у вашому JavaScript-коді на стороні клієнта.
Якщо ви хочете запитати, чи підходить вона для використання eval()
в PHP, відповідь " НІ" , якщо ви не додасте до списку жодних значень, які можуть бути передані вашій заяві eval.
Я не намагатимусь спростувати що-небудь сказане до цього, але я запропоную це використання eval (), яке (наскільки я знаю) неможливо зробити іншим способом. Напевно, є й інші способи кодування цього, і, ймовірно, способи його оптимізації, але це робиться на довгі руки і без жодних дзвінків для наочності, щоб проілюструвати використання eval, що насправді не має інших альтернатив. Тобто: динамічні (або точніше) програмно створені імена об’єктів (на відміну від значень).
//Place this in a common/global JS lib:
var NS = function(namespace){
var namespaceParts = String(namespace).split(".");
var namespaceToTest = "";
for(var i = 0; i < namespaceParts.length; i++){
if(i === 0){
namespaceToTest = namespaceParts[i];
}
else{
namespaceToTest = namespaceToTest + "." + namespaceParts[i];
}
if(eval('typeof ' + namespaceToTest) === "undefined"){
eval(namespaceToTest + ' = {}');
}
}
return eval(namespace);
}
//Then, use this in your class definition libs:
NS('Root.Namespace').Class = function(settings){
//Class constructor code here
}
//some generic method:
Root.Namespace.Class.prototype.Method = function(args){
//Code goes here
//this.MyOtherMethod("foo")); // => "foo"
return true;
}
//Then, in your applications, use this to instantiate an instance of your class:
var anInstanceOfClass = new Root.Namespace.Class(settings);
EDIT: до речі, я б не пропонував (з усіх причин безпеки, зазначених до цього), щоб ви базували об'єкти на імені користувачів. Я не уявляю жодної вагомої причини, що ти хотів би це зробити. І все-таки подумав, що зазначу, що це не буде гарною ідеєю :)
namespaceToTest[namespaceParts[i]]
, тут не потрібно eval, тому if(typeof namespaceToTest[namespaceParts[i]] === 'undefined') { namespaceToTest[namespaceParts[i]] = {};
єдина різниця для користувачаelse namespaceToTest = namespaceToTest[namespaceParts[i]];
Збір сміття
Збір сміття веб-переглядачів не має уявлення про те, чи зможе вилучений код вилучити з пам'яті, щоб він просто зберігався, поки сторінка не буде завантажена. Не надто погано, якщо ваші користувачі незабаром з’являться лише на вашій сторінці, але це може бути проблемою для веб-користувачів.
Ось сценарій для демонстрації проблеми
https://jsfiddle.net/CynderRnAsh/qux1osnw/
document.getElementById("evalLeak").onclick = (e) => {
for(let x = 0; x < 100; x++) {
eval(x.toString());
}
};
Щось таке просто, як наведений вище код, спричиняє збереження невеликої кількості пам’яті, поки додаток не вмирає. Це гірше, коли скрипт, що ухиляється, є гігантською функцією, і викликається на інтервалі.