Функция eval - это мощный и простой способ динамического генерирования кода, так каковы предостережения?
Функция eval - это мощный и простой способ динамического генерирования кода, так каковы предостережения?
Ответы:
Неправильное использование eval открывает ваш код для инъекционных атак
Отладка может быть более сложной (без номеров строк и т. Д.)
eval'd код выполняется медленнее (нет возможности компилировать / кэшировать eval'd код)
Редактировать: Как @Jeff Walden указывает в комментариях, # 3 сегодня менее верен, чем это было в 2008 году. Однако, хотя может происходить некоторое кэширование скомпилированных скриптов, это будет ограничено только скриптами, которые eval-повторялись без модификации. Более вероятным сценарием является то, что вы оцениваете скрипты, которые каждый раз подвергались небольшим изменениям и поэтому не могли быть кэшированы. Давайте просто скажем, что НЕКОТОРЫЙ eval'd код выполняется медленнее.
badHackerGuy'); doMaliciousThings();
и если вы берете мое имя пользователя, объединяете его в некоторый скрипт и оцениваете его в браузерах других людей, тогда я могу запустить любой javascript, который я хочу, на их машинах (например, заставить их добавить +1 к моим сообщениям, опубликовать свои данные на моем сервере и т. д.)
debugger;
оператор в ваш исходный код. Это остановит выполнение вашей программы в этой строке. Затем после этого вы можете добавить точки отладки, как если бы это был просто другой файл JS.
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 прямо из объекта документа.)
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 или input), он будет таким же безопасным и надежным, как и любой другой 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()
, вы можете вместо этого рассмотреть возможность использования новой функции ().
Есть небольшое потенциальное преимущество, потому что код, оцененный в new Function (), будет работать в локальной области функций, поэтому любые переменные, определенные с помощью var в оцениваемом коде, не станут автоматически глобальными.
Другой способ предотвратить автоматические глобальные переменные -
eval()
превратить вызов в непосредственную функцию.
Помимо возможных проблем безопасности, если вы выполняете пользовательский код, в большинстве случаев есть лучший способ, который не требует повторного анализа кода при каждом его выполнении. Анонимные функции или свойства объекта могут заменить большинство применений eval и намного безопаснее и быстрее.
Это может стать более серьезной проблемой, так как браузеры следующего поколения выпускают некоторые разновидности JavaScript-компилятора. Код, выполняемый через Eval, может не работать так же хорошо, как и остальная часть вашего JavaScript на этих новых браузерах. Кто-то должен сделать некоторое профилирование.
Это одна из хороших статей, рассказывающих об eval и о том, как он не является злом: http://www.nczonline.net/blog/2013/06/25/eval-isnt-evil-just-misunderstood/
Я не говорю, что вы должны убежать и начать использовать 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 имеет ряд оптимизаций производительности, которые он выполняет на этапе компиляции. Некоторые из них сводятся к возможности по существу статически анализировать код в процессе его лексического анализа и заранее определять, где находятся все объявления переменных и функций, так что для разрешения идентификаторов во время выполнения требуется меньше усилий.
Но если Механизм находит в коде 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-источник и поместить все нужные ему кусочки в консоль разработчика, чтобы сделать то, что он хочет.
* Пока ваши конечные точки сервера имеют правильную проверку и санацию предоставленных пользователем значений, не должно иметь значения, что анализируется и обрабатывается в вашем клиентском 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);
РЕДАКТИРОВАТЬ: кстати, я бы не советовал (по всем причинам безопасности, указанным ранее), чтобы вы основали свои имена объектов на вводе пользователем. Я не могу представить себе вескую причину, по которой вы захотите это сделать. Тем не менее, я хотел бы указать, что это не будет хорошей идеей :)
namespaceToTest[namespaceParts[i]]
, нет необходимости в Eval здесь, так что if(typeof namespaceToTest[namespaceParts[i]] === 'undefined') { namespaceToTest[namespaceParts[i]] = {};
единственная разница дляelse namespaceToTest = namespaceToTest[namespaceParts[i]];
Вывоз мусора
Сборщик мусора в браузерах не имеет представления, можно ли удалить код из eval'а из памяти, поэтому он просто сохраняет его до перезагрузки страницы. Неплохо, если ваши пользователи скоро появятся на вашей странице, но это может стать проблемой для веб-приложений.
Вот скрипт для демонстрации проблемы
https://jsfiddle.net/CynderRnAsh/qux1osnw/
document.getElementById("evalLeak").onclick = (e) => {
for(let x = 0; x < 100; x++) {
eval(x.toString());
}
};
Что-то такое простое, как приведенный выше код, приводит к тому, что небольшой объем памяти сохраняется до тех пор, пока приложение не умрет. Это хуже, когда уклоненный скрипт является гигантской функцией и вызывается через интервал.