Почему использование JavaScript eval-функции - плохая идея?
Функция eval - это мощный и простой способ динамического генерации кода, поэтому в чем же оговорки?
Ответы
Ответ 1
-
Неправильное использование eval открывает
код для инъекционных атак
-
Отладка может быть более сложной
(номера строк и т.д.)
-
Код eval'd выполняется медленнее (нет возможности компилировать/кэшировать код eval'd)
Изменить: Как отмечает @Jeff Walden в комментариях, # 3 сегодня меньше, чем в 2008 году. Однако, хотя может произойти некоторое кэширование скомпилированных скриптов, это будет ограничено только сценариями, которые повторяются с помощью нет модификация. Более вероятным сценарием является то, что вы оцениваете сценарии, которые каждый раз подвергались незначительной модификации и как таковые не могли быть кэшированы. Скажем, что НЕКОТОРЫЙ код eval'd выполняется медленнее.
Ответ 2
eval не всегда злой. Есть моменты, когда это идеально подходит.
Однако, eval в настоящее время и исторически широко используется людьми, которые не знают, что они делают. К сожалению, это относится к людям, которые, к сожалению, печатают учебники по JavaScript, и в некоторых случаях это может иметь последствия для безопасности - или, чаще, простые ошибки. Итак, чем больше мы можем сделать, чтобы поставить знак вопроса над eval, тем лучше. Каждый раз, когда вы используете eval, вам нужно здравомыслие - проверьте, что вы делаете, потому что, скорее всего, вы можете сделать это лучше, безопаснее и чище.
Чтобы дать типичный пример, чтобы установить цвет элемента с идентификатором, хранящимся в переменной 'potato':
eval('document.' + potato + '.style.color = "red"');
Если авторы такого рода кода имели представление об основах работы JavaScript-объектов, они поняли бы, что вместо литеральных имен точек можно использовать квадратные скобки, устраняя необходимость в eval:
document[potato].style.color = 'red';
..., что намного легче читать, а также менее опасно.
(Но тогда кто-то, кто/действительно/знал, что они делают, сказал бы:
document.getElementById(potato).style.color = 'red';
который более надежен, чем хитроумный старый трюк доступа к элементам DOM прямо из объекта документа.)
Ответ 3
Я верю, потому что он может выполнять любую функцию JavaScript из строки. Использование этого облегчает для людей вводящий в проект код изгоев.
Ответ 4
Приходят на ум две точки:
-
Безопасность (но пока вы создаете строку, которую нужно оценить самостоятельно, это может быть не проблема)
-
Производительность: пока код, который будет запущен, неизвестен, его нельзя оптимизировать. (о javascript и производительности, конечно презентация Steve Yegge)
Ответ 5
Передача пользовательского ввода в eval() представляет собой риск безопасности, но также каждый вызов eval() создает новый экземпляр интерпретатора JavaScript. Это может быть ресурс hog.
Ответ 6
Как правило, это проблема только в том случае, если вы передаете пользовательский ввод eval.
Ответ 7
В основном, это намного сложнее поддерживать и отлаживать. Это как goto
. Вы можете использовать его, но это затрудняет поиск проблем и труднее для людей, которым, возможно, потребуется внести изменения позже.
Ответ 8
Следует иметь в виду, что вы часто можете использовать eval() для выполнения кода в условиях, которые в противном случае ограничены - сайты социальных сетей, которые блокируют определенные функции JavaScript, иногда можно обмануть, разбив их на блок eval -
eval('al' + 'er' + 't(\'' + 'hi there!' + '\')');
Поэтому, если вы хотите запустить какой-то JavaScript-код, где это может не разрешаться иначе (Myspace, я смотрю на вас...), то eval() может быть полезным трюком.
Тем не менее, по всем причинам, упомянутым выше, вы не должны использовать его для своего собственного кода, где у вас есть полный контроль - он просто не нужен, и более обеспеченный, отведенный на полку "сложных хаков JavaScript".
Ответ 9
Если вы не дадите eval() динамический контент (через cgi или ввод), он будет таким же безопасным и надежным, как и весь другой JavaScript на вашей странице.
Ответ 10
Наряду с остальными ответами, я не думаю, что утверждения eval могут иметь предварительную минимизацию.
Ответ 11
Это возможный риск для безопасности, он имеет различную область исполнения и довольно неэффективен, так как он создает совершенно новую среду сценариев для выполнения кода. См. Здесь дополнительную информацию: eval.
Это очень полезно, однако, и используется с умеренностью, может добавить много хорошей функциональности.
Ответ 12
Если вы на 100% не уверены, что оцениваемый код принадлежит доверенному источнику (обычно вашему собственному приложению), то он является верным способом подвергнуть вашу систему межсайтовой скриптовой атаке.
Ответ 13
Это не обязательно так плохо, если вы знаете, в каком контексте вы его используете.
Если ваше приложение использует eval()
для создания объекта из некоторого JSON, который вернулся из XMLHttpRequest в свой собственный сайт, созданный вашим доверенным кодом на стороне сервера, это, вероятно, не проблема.
Неверный клиентский JavaScript-код не может так много сделать. Если вещь, которую вы выполняете eval()
on, исходила из разумного источника, вы в порядке.
Ответ 14
Я знаю, что это обсуждение устарело, но мне очень нравится этот подход Google и хотел поделиться этим чувством с другими;)
Другое дело, что чем лучше вы получаете больше, тем больше вы пытаетесь понять, и, наконец, вы просто не верите, что что-то хорошо или плохо, потому что кто-то сказал так:)
Это очень вдохновляющий видео, который помог мне больше поразмышлять над собой:) ХОРОШАЯ ПРАКТИКА хороша, но не использует их бесполезно:)
Ответ 15
Это значительно снижает уровень уверенности в безопасности.
Ответ 16
Если вы хотите, чтобы пользователь вводил некоторые логические функции и оценивал AND и OR, функция JavaScript eval идеально подходит. Я могу принять две строки и eval(uate) string1 === string2
и т.д.
Ответ 17
Если вы заметили использование 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()
, вы можете вместо этого использовать новую функцию().
Существует небольшой потенциал
потому что код, оцененный в новой функции(), будет запущен в локальной функции
scope, поэтому любые переменные, определенные с var в оцениваемом коде, не станут
глобальные переменные автоматически.
Другой способ предотвратить автоматические глобальные переменные - обернуть
eval()
вызов в непосредственную функцию.
Ответ 18
Помимо возможных проблем с безопасностью, если вы выполняете код, отправленный пользователем, большую часть времени там есть лучший способ, который не требует повторного разбора кода каждый раз, когда он выполняется. Анонимные функции или свойства объектов могут заменить большинство применений eval и намного безопаснее и быстрее.
Ответ 19
Это может стать более проблематичным, поскольку следующее поколение браузеров выходит с некоторым вкусом компилятора JavaScript. Код, выполняемый через Eval, может не работать так же, как и остальная часть вашего JavaScript, в отношении этих новых браузеров. Кто-то должен сделать профилирование.
Ответ 20
Это одна из хороших статей, рассказывающих об eval и о том, как это не зло:
http://www.nczonline.net/blog/2013/06/25/eval-isnt-evil-just-misunderstood/
Я не говорю, что вам следует сбежать и начать использовать eval() везде. На самом деле, существует очень мало хороших вариантов использования eval(). Есть определенная проблема с ясностью кода, отлаживаемость и, конечно же, производительность, которую нельзя игнорировать. Но вы не должны бояться использовать его, когда у вас есть случай, когда eval() имеет смысл. Старайтесь не использовать его сначала, но не позволяйте никому пугать вы считаете, что ваш код более хрупкий или менее безопасный, когда eval() используется надлежащим образом.
Ответ 21
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
Ответ 22
Я не буду пытаться опровергнуть сказанное до сих пор, но я буду предлагать это использование 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: Кстати, я бы не предложил (по всем причинам безопасности, о которых говорилось выше), что вы основываете свои имена объектов на вводе пользователя. Я не могу представить, какая из веских причин вам хотелось бы сделать это. Тем не менее, подумал, что я бы сказал, что это не будет хорошей идеей:)
Ответ 23
У JavaScript Engine есть ряд оптимизаций производительности, которые он выполняет на этапе компиляции. Некоторые из них сводятся к тому, что они могут существенно статически анализировать код, поскольку он лексирует, и предварительно определяют, где все объявления переменных и функций, так что требуется меньше усилий для устранения идентификаторов во время выполнения.
Но если Engine находит eval (..) в коде, он, по существу, должен предположить, что вся его осведомленность о местоположении идентификатора может быть недействительной, поскольку она не может знать во время lexing точно, какой код вы можете передать в 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
Ответ 24
Это не всегда плохая идея. Возьмем, к примеру, генерацию кода. Недавно я написал библиотеку под названием Hyperbars, которая устраняет разрыв между virtual-dom и handlebars. Он делает это путем анализа шаблона руля и преобразования его в hyperscript, который впоследствии используется виртуальным 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()
не является проблемой в подобной ситуации, потому что вам нужно только один раз интерпретировать сгенерированную строку, а затем повторно использовать исполняемый вывод многократно.
Вы можете видеть, как генерация кода была достигнута, если вам любопытно здесь.
Ответ 25
Я бы сказал, что это не имеет значения, если вы используете eval()
в javascript, который запускается в браузерах. * (caveat)
Во всех современных браузерах есть консоль разработчика, где вы можете произвольно выполнить произвольный javascript, и любой полу-умный разработчик может посмотреть на ваш источник JS и поместить все необходимые ему биты в консоль dev, чтобы делать то, что они хотят.
* Пока конечные точки вашего сервера имеют правильную проверку и санитацию значений, предоставленных пользователем, не должно иметь значения, что получает синтаксический анализ и eval'd на вашем javascript стороне клиента.
Если вы спросите, подходит ли это для использования eval()
в PHP, то ответ будет НЕТ, если вы не белый список не можете присвоить значения, которые могут быть переданы на ваш eval.
Ответ 26
Сборка мусора
Сборщик мусора в браузерах понятия не имеет, можно ли удалить eval'ed-код из памяти, поэтому он просто сохраняет его до перезагрузки страницы.
Неплохо, если ваши пользователи скоро появятся на вашей странице, но это может стать проблемой для веб-приложений.
Здесь скрипт для демонстрации проблемы
https://jsfiddle.net/CynderRnAsh/qux1osnw/
document.getElementById("evalLeak").onclick = (e) => {
for(let x = 0; x < 100; x++) {
eval(x.toString());
}
};
Нечто простое, как в приведенном выше коде, приводит к тому, что небольшой объем памяти сохраняется до тех пор, пока приложение не умрет.
Это хуже, когда уклоненный скрипт является гигантской функцией и вызывается через интервал.